veltro-cli 0.15.7__py3-none-any.whl
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.
- veltro_cli/__init__.py +6 -0
- veltro_cli/__main__.py +7 -0
- veltro_cli/cli.py +478 -0
- veltro_cli/lib/__init__.py +3 -0
- veltro_cli/lib/agent.py +37 -0
- veltro_cli/lib/backup.py +139 -0
- veltro_cli/lib/compose.py +276 -0
- veltro_cli/lib/datasets.py +611 -0
- veltro_cli/lib/demo.py +421 -0
- veltro_cli/lib/demo_replay.py +794 -0
- veltro_cli/lib/doctor.py +99 -0
- veltro_cli/lib/env_init.py +93 -0
- veltro_cli/lib/errors.py +40 -0
- veltro_cli/lib/host.py +346 -0
- veltro_cli/lib/install.py +400 -0
- veltro_cli/lib/k8s.py +57 -0
- veltro_cli/lib/paths.py +42 -0
- veltro_cli/lib/products.py +54 -0
- veltro_cli/lib/release.py +88 -0
- veltro_cli/lib/status.py +170 -0
- veltro_cli/lib/suite_http.py +227 -0
- veltro_cli/lib/support.py +35 -0
- veltro_cli/lib/tls.py +113 -0
- veltro_cli/lib/toml_config.py +130 -0
- veltro_cli/lib/upgrade.py +379 -0
- veltro_cli/py.typed +0 -0
- veltro_cli-0.15.7.dist-info/METADATA +40 -0
- veltro_cli-0.15.7.dist-info/RECORD +31 -0
- veltro_cli-0.15.7.dist-info/WHEEL +5 -0
- veltro_cli-0.15.7.dist-info/entry_points.txt +2 -0
- veltro_cli-0.15.7.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,379 @@
|
|
|
1
|
+
"""Fetch and apply one immutable Veltro suite release."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import hashlib
|
|
6
|
+
import json
|
|
7
|
+
import os
|
|
8
|
+
import re
|
|
9
|
+
import subprocess
|
|
10
|
+
import sys
|
|
11
|
+
import tempfile
|
|
12
|
+
import urllib.request
|
|
13
|
+
from datetime import datetime, timezone
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
from typing import Any, Callable, Mapping
|
|
16
|
+
|
|
17
|
+
from veltro_cli.lib.compose import ComposeClient
|
|
18
|
+
from veltro_cli.lib.errors import VeltroCliError
|
|
19
|
+
from veltro_cli.lib.install import materialise_from_checkout, materialise_from_tarball
|
|
20
|
+
from veltro_cli.lib.paths import resolve_dir
|
|
21
|
+
from veltro_cli.lib.products import read_env_file, resolve_selection
|
|
22
|
+
from veltro_cli.lib.release import (
|
|
23
|
+
RELEASE_BASE,
|
|
24
|
+
RELEASE_REPO,
|
|
25
|
+
parse_release_version,
|
|
26
|
+
release_tag,
|
|
27
|
+
release_version,
|
|
28
|
+
)
|
|
29
|
+
from veltro_cli.lib.toml_config import VELTRO_TOML, read_config, write_config
|
|
30
|
+
|
|
31
|
+
VERSION_VARS = {
|
|
32
|
+
"vectorflow": "VF_VERSION",
|
|
33
|
+
"chad": "CHAD_VERSION",
|
|
34
|
+
"warden": "WARDEN_VERSION",
|
|
35
|
+
"veltro": "VELTRO_VERSION",
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def normalise_target(target: str) -> tuple[str, str]:
|
|
40
|
+
"""``(tag, X.Y.Z)`` for a release named by its version or either tag spelling."""
|
|
41
|
+
version = release_version(target)
|
|
42
|
+
return release_tag(version), version
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def refuse_downgrade(current: str, target: str) -> None:
|
|
46
|
+
cur = parse_release_version(current)
|
|
47
|
+
tgt = parse_release_version(target)
|
|
48
|
+
if tgt < cur:
|
|
49
|
+
raise VeltroCliError(
|
|
50
|
+
f"refusing downgrade: installed {current} → target {target}",
|
|
51
|
+
remedy="restore from backup if you need an older suite; upgrade never rolls backward",
|
|
52
|
+
)
|
|
53
|
+
if tgt == cur:
|
|
54
|
+
raise VeltroCliError(
|
|
55
|
+
f"already at {current}",
|
|
56
|
+
remedy="pass a newer --to X.Y.Z",
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def fetch_release_notes(body: str) -> list[str]:
|
|
61
|
+
warnings: list[str] = []
|
|
62
|
+
for heading in ("BREAKING", "MIGRATION"):
|
|
63
|
+
if re.search(rf"^#+ .*{heading}", body, re.I | re.M):
|
|
64
|
+
warnings.append(heading)
|
|
65
|
+
return warnings
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def release_urls(target: str) -> tuple[str, str]:
|
|
69
|
+
tag, version = normalise_target(target)
|
|
70
|
+
return (
|
|
71
|
+
f"{RELEASE_BASE}/releases/download/{tag}/veltro-suite-{version}.json",
|
|
72
|
+
f"{RELEASE_BASE}/archive/refs/tags/{tag}.tar.gz",
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def load_release_manifest(
|
|
77
|
+
target: str,
|
|
78
|
+
*,
|
|
79
|
+
opener: Callable[..., Any] = urllib.request.urlopen,
|
|
80
|
+
) -> dict[str, Any]:
|
|
81
|
+
manifest_url, _ = release_urls(target)
|
|
82
|
+
try:
|
|
83
|
+
with opener(manifest_url, timeout=30) as response:
|
|
84
|
+
raw = response.read()
|
|
85
|
+
except Exception as exc:
|
|
86
|
+
raise VeltroCliError(
|
|
87
|
+
f"could not fetch suite manifest for {target}: {exc}",
|
|
88
|
+
remedy=f"verify the release exists at {manifest_url} and retry",
|
|
89
|
+
) from exc
|
|
90
|
+
try:
|
|
91
|
+
manifest = json.loads(raw)
|
|
92
|
+
except (UnicodeDecodeError, json.JSONDecodeError) as exc:
|
|
93
|
+
raise VeltroCliError(
|
|
94
|
+
"published suite manifest is not valid JSON",
|
|
95
|
+
remedy="do not upgrade; report the immutable release asset",
|
|
96
|
+
) from exc
|
|
97
|
+
_, version = normalise_target(target)
|
|
98
|
+
if not isinstance(manifest, dict) or manifest.get("schema") not in {
|
|
99
|
+
"veltro-suite-manifest/v4",
|
|
100
|
+
"veltro-suite-manifest/v5",
|
|
101
|
+
}:
|
|
102
|
+
raise VeltroCliError("published suite manifest has an unsupported schema")
|
|
103
|
+
if manifest.get("suite_version") != version:
|
|
104
|
+
raise VeltroCliError(
|
|
105
|
+
f"manifest suite_version {manifest.get('suite_version')!r} does not match {version!r}",
|
|
106
|
+
remedy="do not upgrade; the release asset is inconsistent with its tag",
|
|
107
|
+
)
|
|
108
|
+
verification = manifest.get("verification")
|
|
109
|
+
if not isinstance(verification, dict) or verification.get("status") not in {
|
|
110
|
+
"smoke-passed",
|
|
111
|
+
"config-rendered",
|
|
112
|
+
}:
|
|
113
|
+
raise VeltroCliError(
|
|
114
|
+
"suite manifest is not release-gate attested",
|
|
115
|
+
remedy="choose a published suite release with verification evidence",
|
|
116
|
+
)
|
|
117
|
+
return manifest
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def manifest_versions(manifest: Mapping[str, Any]) -> dict[str, str]:
|
|
121
|
+
modules = manifest.get("modules")
|
|
122
|
+
if not isinstance(modules, dict):
|
|
123
|
+
raise VeltroCliError("suite manifest has no module versions")
|
|
124
|
+
versions: dict[str, str] = {}
|
|
125
|
+
for name, env_var in VERSION_VARS.items():
|
|
126
|
+
module = modules.get(name)
|
|
127
|
+
if not isinstance(module, dict) or not isinstance(module.get("version"), str):
|
|
128
|
+
raise VeltroCliError(f"suite manifest has no {name} version")
|
|
129
|
+
versions[env_var] = module["version"]
|
|
130
|
+
return versions
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def verify_staged_pins(root: Path, manifest: Mapping[str, Any]) -> None:
|
|
134
|
+
actual = read_env_file(root / "versions.env")
|
|
135
|
+
expected = manifest_versions(manifest)
|
|
136
|
+
mismatches = [
|
|
137
|
+
f"{key}={actual.get(key)!r} (manifest {value!r})"
|
|
138
|
+
for key, value in expected.items()
|
|
139
|
+
if actual.get(key) != value
|
|
140
|
+
]
|
|
141
|
+
if mismatches:
|
|
142
|
+
raise VeltroCliError(
|
|
143
|
+
"release tarball pins disagree with its manifest: " + ", ".join(mismatches),
|
|
144
|
+
remedy="do not upgrade; report the immutable release artifacts",
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def verify_staged_release(root: Path, manifest: Mapping[str, Any]) -> None:
|
|
149
|
+
"""Bind extracted release bytes to the release-gated manifest."""
|
|
150
|
+
verify_staged_pins(root, manifest)
|
|
151
|
+
contract = manifest.get("config_contract")
|
|
152
|
+
files = contract.get("files") if isinstance(contract, dict) else None
|
|
153
|
+
if not isinstance(files, dict) or not files:
|
|
154
|
+
raise VeltroCliError(
|
|
155
|
+
"suite manifest has no config artifact digests",
|
|
156
|
+
remedy="do not install or upgrade from an unattested release",
|
|
157
|
+
)
|
|
158
|
+
for relative, expected in files.items():
|
|
159
|
+
if not isinstance(relative, str) or not isinstance(expected, str):
|
|
160
|
+
raise VeltroCliError("suite manifest has a malformed config artifact digest")
|
|
161
|
+
path = root / relative
|
|
162
|
+
actual = "sha256:" + hashlib.sha256(path.read_bytes()).hexdigest() if path.is_file() else None
|
|
163
|
+
if actual != expected:
|
|
164
|
+
raise VeltroCliError(
|
|
165
|
+
f"release artifact {relative} digest {actual!r} does not match {expected!r}",
|
|
166
|
+
remedy="do not install or upgrade; report the immutable release artifacts",
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
def _run_backup(root: Path, products: tuple[str, ...], staged: Path) -> Path:
|
|
170
|
+
stamp = datetime.now(timezone.utc).strftime("%Y%m%dT%H%M%S%fZ")
|
|
171
|
+
archive = root / "backups" / f"veltro-backup-auto-{stamp}.tar"
|
|
172
|
+
archive.parent.mkdir(parents=True, exist_ok=True)
|
|
173
|
+
command = [
|
|
174
|
+
sys.executable,
|
|
175
|
+
str(staged / "scripts" / "veltro_backup.py"),
|
|
176
|
+
"backup",
|
|
177
|
+
"--dir",
|
|
178
|
+
str(root),
|
|
179
|
+
"--products",
|
|
180
|
+
",".join(products),
|
|
181
|
+
"--to",
|
|
182
|
+
str(archive),
|
|
183
|
+
]
|
|
184
|
+
# The old checkout still has legacy backup code and profile names. Run the
|
|
185
|
+
# staged backup against old pins/.env, but render the staged Compose services
|
|
186
|
+
# under the existing project name so exec finds the running containers.
|
|
187
|
+
env = dict(os.environ)
|
|
188
|
+
env["COMPOSE_FILE"] = str(staged / "docker-compose.yml")
|
|
189
|
+
env["COMPOSE_PROJECT_NAME"] = (
|
|
190
|
+
env.get("COMPOSE_PROJECT_NAME")
|
|
191
|
+
or read_env_file(root / ".env").get("COMPOSE_PROJECT_NAME")
|
|
192
|
+
or root.name
|
|
193
|
+
)
|
|
194
|
+
completed = subprocess.run(command, text=True, capture_output=True, check=False, env=env)
|
|
195
|
+
if completed.returncode != 0:
|
|
196
|
+
raise VeltroCliError(
|
|
197
|
+
completed.stderr.strip() or "automatic pre-upgrade backup failed",
|
|
198
|
+
remedy="fix backup prerequisites; upgrade has not changed the install",
|
|
199
|
+
)
|
|
200
|
+
if not archive.is_file():
|
|
201
|
+
raise VeltroCliError(
|
|
202
|
+
f"automatic backup reported success but did not create {archive}",
|
|
203
|
+
remedy="upgrade has not changed the install; inspect the backup command output",
|
|
204
|
+
)
|
|
205
|
+
return archive
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
def expected_image_digests(manifest: Mapping[str, Any]) -> dict[str, str]:
|
|
209
|
+
expected: dict[str, str] = {}
|
|
210
|
+
modules = manifest.get("modules")
|
|
211
|
+
if isinstance(modules, dict):
|
|
212
|
+
for module in modules.values():
|
|
213
|
+
if not isinstance(module, dict):
|
|
214
|
+
continue
|
|
215
|
+
images = module.get("images")
|
|
216
|
+
if isinstance(images, dict):
|
|
217
|
+
expected.update(
|
|
218
|
+
{
|
|
219
|
+
ref: digest
|
|
220
|
+
for ref, digest in images.items()
|
|
221
|
+
if isinstance(ref, str)
|
|
222
|
+
and isinstance(digest, str)
|
|
223
|
+
and digest.startswith("sha256:")
|
|
224
|
+
}
|
|
225
|
+
)
|
|
226
|
+
return expected
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
def verify_local_image_digests(
|
|
230
|
+
manifest: Mapping[str, Any],
|
|
231
|
+
*,
|
|
232
|
+
runner: Callable[..., subprocess.CompletedProcess[str]] = subprocess.run,
|
|
233
|
+
) -> None:
|
|
234
|
+
for ref, digest in expected_image_digests(manifest).items():
|
|
235
|
+
completed = runner(
|
|
236
|
+
["docker", "image", "inspect", "--format", "{{json .RepoDigests}}", ref],
|
|
237
|
+
text=True,
|
|
238
|
+
capture_output=True,
|
|
239
|
+
check=False,
|
|
240
|
+
)
|
|
241
|
+
if completed.returncode != 0 or f"@{digest}" not in completed.stdout:
|
|
242
|
+
raise VeltroCliError(
|
|
243
|
+
f"running image {ref} is not the manifest digest {digest}",
|
|
244
|
+
remedy="run `veltro down`, restore the automatic backup, and inspect registry access",
|
|
245
|
+
)
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
def module_version_deltas(root: Path, manifest: Mapping[str, Any]) -> list[dict[str, str]]:
|
|
249
|
+
current = read_env_file(root / "versions.env")
|
|
250
|
+
target = manifest_versions(manifest)
|
|
251
|
+
return [
|
|
252
|
+
{"module": name, "from": current.get(env_var, "unknown"), "to": target[env_var]}
|
|
253
|
+
for name, env_var in VERSION_VARS.items()
|
|
254
|
+
if current.get(env_var) != target[env_var]
|
|
255
|
+
]
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
def load_release_warnings(
|
|
259
|
+
target: str,
|
|
260
|
+
manifest: Mapping[str, Any],
|
|
261
|
+
*,
|
|
262
|
+
opener: Callable[..., Any] = urllib.request.urlopen,
|
|
263
|
+
) -> list[str]:
|
|
264
|
+
tag, _ = normalise_target(target)
|
|
265
|
+
releases: list[tuple[str, str, str]] = [("suite", RELEASE_REPO, tag)]
|
|
266
|
+
modules = manifest.get("modules")
|
|
267
|
+
if isinstance(modules, dict):
|
|
268
|
+
for name, module in modules.items():
|
|
269
|
+
if isinstance(module, dict) and isinstance(module.get("repo"), str) and isinstance(module.get("tag"), str):
|
|
270
|
+
releases.append((name, module["repo"], module["tag"]))
|
|
271
|
+
warnings: list[str] = []
|
|
272
|
+
for name, repo, notes_tag in releases:
|
|
273
|
+
request = urllib.request.Request(
|
|
274
|
+
f"https://api.github.com/repos/{repo}/releases/tags/{notes_tag}",
|
|
275
|
+
headers={"Accept": "application/vnd.github+json"},
|
|
276
|
+
)
|
|
277
|
+
try:
|
|
278
|
+
with opener(request, timeout=15) as response:
|
|
279
|
+
payload = json.load(response)
|
|
280
|
+
except Exception as exc:
|
|
281
|
+
warnings.append(f"{name} release notes unavailable: {exc}")
|
|
282
|
+
continue
|
|
283
|
+
body = payload.get("body") if isinstance(payload, dict) else ""
|
|
284
|
+
for heading in fetch_release_notes(body if isinstance(body, str) else ""):
|
|
285
|
+
warnings.append(f"{name}: {heading} section present in release notes")
|
|
286
|
+
return warnings
|
|
287
|
+
|
|
288
|
+
def plan_upgrade(
|
|
289
|
+
*,
|
|
290
|
+
directory: str | Path | None,
|
|
291
|
+
target: str,
|
|
292
|
+
current: str | None = None,
|
|
293
|
+
manifest_opener: Callable[..., Any] = urllib.request.urlopen,
|
|
294
|
+
notes_opener: Callable[..., Any] = urllib.request.urlopen,
|
|
295
|
+
) -> dict[str, Any]:
|
|
296
|
+
root = resolve_dir(directory)
|
|
297
|
+
cfg = read_config(root / VELTRO_TOML)
|
|
298
|
+
installed = current or (cfg.installed_suite_version if cfg else "") or "0.0.0"
|
|
299
|
+
refuse_downgrade(installed, target)
|
|
300
|
+
manifest = load_release_manifest(target, opener=manifest_opener)
|
|
301
|
+
return {
|
|
302
|
+
"directory": str(root),
|
|
303
|
+
"from": installed,
|
|
304
|
+
"to": normalise_target(target)[0],
|
|
305
|
+
"dry_run": True,
|
|
306
|
+
"module_deltas": module_version_deltas(root, manifest),
|
|
307
|
+
"warnings": load_release_warnings(target, manifest, opener=notes_opener),
|
|
308
|
+
"steps": [
|
|
309
|
+
"fetch and verify immutable suite manifest",
|
|
310
|
+
"veltro backup --auto",
|
|
311
|
+
"replace compose/nginx/versions.env from release",
|
|
312
|
+
"veltro up --wait",
|
|
313
|
+
"verify local image digests against suite manifest",
|
|
314
|
+
"record installed_version in veltro.toml",
|
|
315
|
+
],
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
|
|
319
|
+
def execute_upgrade(
|
|
320
|
+
*,
|
|
321
|
+
directory: str | Path | None,
|
|
322
|
+
target: str,
|
|
323
|
+
current: str | None = None,
|
|
324
|
+
manifest_opener: Callable[..., Any] = urllib.request.urlopen,
|
|
325
|
+
tarball_opener: Callable[..., Any] = urllib.request.urlopen,
|
|
326
|
+
backup_runner: Callable[[Path, tuple[str, ...], Path], Path] = _run_backup,
|
|
327
|
+
client_factory: Callable[..., ComposeClient] = ComposeClient,
|
|
328
|
+
digest_verifier: Callable[[Mapping[str, Any]], None] = verify_local_image_digests,
|
|
329
|
+
) -> dict[str, Any]:
|
|
330
|
+
root = resolve_dir(directory)
|
|
331
|
+
cfg = read_config(root / VELTRO_TOML)
|
|
332
|
+
if cfg is None:
|
|
333
|
+
raise VeltroCliError("veltro.toml is missing", remedy="run `veltro install` first")
|
|
334
|
+
installed = current or cfg.installed_suite_version or "0.0.0"
|
|
335
|
+
refuse_downgrade(installed, target)
|
|
336
|
+
manifest = load_release_manifest(target, opener=manifest_opener)
|
|
337
|
+
_, tarball_url = release_urls(target)
|
|
338
|
+
products = resolve_selection(root)
|
|
339
|
+
with tempfile.TemporaryDirectory(prefix="veltro-upgrade-") as tmp:
|
|
340
|
+
staged = Path(tmp) / "release"
|
|
341
|
+
materialise_from_tarball(tarball_url, staged, opener=tarball_opener)
|
|
342
|
+
verify_staged_release(staged, manifest)
|
|
343
|
+
backup_path = backup_runner(root, products, staged)
|
|
344
|
+
materialise_from_checkout(staged, root)
|
|
345
|
+
if cfg.tls_mode != "off":
|
|
346
|
+
from veltro_cli.lib.tls import write_caddyfile
|
|
347
|
+
|
|
348
|
+
write_caddyfile(
|
|
349
|
+
root,
|
|
350
|
+
tls_mode=cfg.tls_mode,
|
|
351
|
+
public_url=cfg.public_url,
|
|
352
|
+
products=products,
|
|
353
|
+
)
|
|
354
|
+
client = client_factory(
|
|
355
|
+
root,
|
|
356
|
+
products=products,
|
|
357
|
+
tls_mode=cfg.tls_mode,
|
|
358
|
+
monitoring=cfg.monitoring,
|
|
359
|
+
)
|
|
360
|
+
result = client.up()
|
|
361
|
+
if result.returncode != 0:
|
|
362
|
+
raise VeltroCliError(
|
|
363
|
+
result.stderr.strip() or "upgraded services did not become healthy",
|
|
364
|
+
remedy=f"run `veltro restore {backup_path} --yes`",
|
|
365
|
+
)
|
|
366
|
+
digest_verifier(manifest)
|
|
367
|
+
tag, _ = normalise_target(target)
|
|
368
|
+
write_config(
|
|
369
|
+
root / VELTRO_TOML,
|
|
370
|
+
type(cfg)(
|
|
371
|
+
products=",".join(products),
|
|
372
|
+
public_url=cfg.public_url,
|
|
373
|
+
tls_mode=cfg.tls_mode,
|
|
374
|
+
dir=cfg.dir,
|
|
375
|
+
installed_suite_version=tag,
|
|
376
|
+
monitoring=cfg.monitoring,
|
|
377
|
+
),
|
|
378
|
+
)
|
|
379
|
+
return {"from": installed, "to": tag, "backup": str(backup_path)}
|
veltro_cli/py.typed
ADDED
|
File without changes
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: veltro-cli
|
|
3
|
+
Version: 0.15.7
|
|
4
|
+
Summary: Operator CLI for the Veltro security operations suite (Compose + Helm).
|
|
5
|
+
Author: Veltro Security
|
|
6
|
+
License: AGPL-3.0-only
|
|
7
|
+
Project-URL: Homepage, https://github.com/veltrosecurity/veltro
|
|
8
|
+
Project-URL: Documentation, https://github.com/veltrosecurity/veltro#readme
|
|
9
|
+
Project-URL: Repository, https://github.com/veltrosecurity/veltro
|
|
10
|
+
Project-URL: Issues, https://github.com/veltrosecurity/veltro/issues
|
|
11
|
+
Keywords: veltro,security,soc,compose,helm
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Environment :: Console
|
|
14
|
+
Classifier: Intended Audience :: System Administrators
|
|
15
|
+
Classifier: License :: OSI Approved :: GNU Affero General Public License v3
|
|
16
|
+
Classifier: Operating System :: POSIX
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
22
|
+
Classifier: Topic :: Security
|
|
23
|
+
Classifier: Topic :: System :: Systems Administration
|
|
24
|
+
Requires-Python: >=3.10
|
|
25
|
+
Description-Content-Type: text/markdown
|
|
26
|
+
|
|
27
|
+
# veltro-cli
|
|
28
|
+
|
|
29
|
+
Stdlib-only operator CLI for the Veltro suite. Every Veltro release publishes
|
|
30
|
+
it at the release's version. Install a release with:
|
|
31
|
+
|
|
32
|
+
```sh
|
|
33
|
+
curl -fsSL https://raw.githubusercontent.com/veltrosecurity/veltro/vX.Y.Z/install.sh \
|
|
34
|
+
| VELTRO_RELEASE=X.Y.Z sh
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
or `pip install veltro-cli==X.Y.Z` and `veltro install --suite-version X.Y.Z`.
|
|
38
|
+
Leave the version out to take the newest release.
|
|
39
|
+
|
|
40
|
+
Requires Python ≥ 3.10 and Docker Compose ≥ 2.20 on the host.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
veltro_cli/__init__.py,sha256=VnqEClGNNd-QoD4KB-KKrdl4MiGH7UE79FDD29io9aY,162
|
|
2
|
+
veltro_cli/__main__.py,sha256=3TsZ7P9l58oD0WSz5Bs123zv4awf_A7F_zFLhjYktZ4,122
|
|
3
|
+
veltro_cli/cli.py,sha256=h4ktX-TGiUpnDfGIEJV1yizmtqw1IKm_jfh1c2Vq8Xk,18999
|
|
4
|
+
veltro_cli/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
5
|
+
veltro_cli/lib/__init__.py,sha256=AfQgBH4JgPETDMOLmeY2mueXU8EPL-M9O76lpf0d0lE,98
|
|
6
|
+
veltro_cli/lib/agent.py,sha256=7U8c5x8e50jgU_BBlCpcuUNn_9RcZKzDUC6peRK65y4,1358
|
|
7
|
+
veltro_cli/lib/backup.py,sha256=Xu6XjaFxiwHKald_WyUvY5ZLA72bAegCqfkFGdtES7U,4789
|
|
8
|
+
veltro_cli/lib/compose.py,sha256=-m9xsqRF7Dn4dryk-dkBwtaLvpVpZ4dndyAyo0mHC78,9316
|
|
9
|
+
veltro_cli/lib/datasets.py,sha256=BLGZYXnQ7HBbjl4KKO6b6SyWyBa9r6IMcsvmFLbB-Kc,23622
|
|
10
|
+
veltro_cli/lib/demo.py,sha256=gipzWJc9R-Na4h75rL6QIZpxQeAQn-6_wUsYaaPCf4g,16076
|
|
11
|
+
veltro_cli/lib/demo_replay.py,sha256=i6PLEKFPCobcE_-PEYKSVcqr5ipUdnD_q8Mab1Drd20,32353
|
|
12
|
+
veltro_cli/lib/doctor.py,sha256=KvOJlvGkg2MN4gy7nf1ODuMuEMZNuetB7LZI2sJC4bM,3097
|
|
13
|
+
veltro_cli/lib/env_init.py,sha256=27J8NfiLLasSRGQ79dtskRb3Dum6xjvI21YqdwcwJqg,3416
|
|
14
|
+
veltro_cli/lib/errors.py,sha256=uUEtGpdMLSM5_A8S_u-Gg2zGZ1uaZVluMHUNzJZrvsw,1083
|
|
15
|
+
veltro_cli/lib/host.py,sha256=GQdnNppsYqaI26yFDIE0E_2mpeyjGbBTdeZa3QlrgwY,11267
|
|
16
|
+
veltro_cli/lib/install.py,sha256=2CghrBRB7t8SlBm4P9YC5UbcIATTanwbvu0TyjEklJs,15287
|
|
17
|
+
veltro_cli/lib/k8s.py,sha256=WzpsrMjqgUIRzI19Ywy2xs4FcH45PPq30CLdCT8IamI,2026
|
|
18
|
+
veltro_cli/lib/paths.py,sha256=n2SzPs-0-vJant86WIpnHbg44j3Dhtt0YKBoTbWvqXM,1489
|
|
19
|
+
veltro_cli/lib/products.py,sha256=XtyJHSA_tXLLMtyTO72qmeOsX_8yoM_Qris6-gcUrqE,2037
|
|
20
|
+
veltro_cli/lib/release.py,sha256=uj4ltZfSFkI0A8VtWrKFBCEE-DaWYX4uJXS0Rcb0wsQ,3303
|
|
21
|
+
veltro_cli/lib/status.py,sha256=W4Z7ONM46XXdswVNOuTqY8b56fnAvexyzMxI8_eg-68,6434
|
|
22
|
+
veltro_cli/lib/suite_http.py,sha256=x8nVimYw9OcG1MfnYEFOrPGWkdvdgbW41kTeDFFuRsA,9354
|
|
23
|
+
veltro_cli/lib/support.py,sha256=rqLZAiAaps-JiJx4CWttMnzlnKEnA9aAHRbDHGL9zdA,1169
|
|
24
|
+
veltro_cli/lib/tls.py,sha256=-h4M3SUH_7_ZL8xnvXYCZQ5QXgC7PXhtSqi2u4BUjzU,3675
|
|
25
|
+
veltro_cli/lib/toml_config.py,sha256=pWqlJnaOEno5fVivVTtkbsvLW13IYy9PjvCSvKc1LXc,3822
|
|
26
|
+
veltro_cli/lib/upgrade.py,sha256=LtPumvD6DrxQid6TfHKUpmW12LjGBLweCEcQKHXlgek,14519
|
|
27
|
+
veltro_cli-0.15.7.dist-info/METADATA,sha256=fsjKbb8oGyNgsgVdwP0XI2Z1eD2ylCDdvHVlCiLC--w,1622
|
|
28
|
+
veltro_cli-0.15.7.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
|
|
29
|
+
veltro_cli-0.15.7.dist-info/entry_points.txt,sha256=YAiC7EzAE1y2ekqMrqzKXjTzfC61Dre003Lq_UFKH5Y,47
|
|
30
|
+
veltro_cli-0.15.7.dist-info/top_level.txt,sha256=WhGjLaHoeUKxcuWg-8GxmhXJc61uPYz3l4OCWU_nGBE,11
|
|
31
|
+
veltro_cli-0.15.7.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
veltro_cli
|