tecrax 0.2.2a0__tar.gz → 0.3.2a0__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.
- {tecrax-0.2.2a0 → tecrax-0.3.2a0}/.github/workflows/ci.yml +5 -0
- {tecrax-0.2.2a0 → tecrax-0.3.2a0}/PKG-INFO +32 -13
- tecrax-0.3.2a0/PUBLIC_STATUS.md +7 -0
- {tecrax-0.2.2a0 → tecrax-0.3.2a0}/README.md +25 -9
- {tecrax-0.2.2a0 → tecrax-0.3.2a0}/VALIDATION.md +5 -3
- {tecrax-0.2.2a0 → tecrax-0.3.2a0}/pyproject.toml +18 -4
- {tecrax-0.2.2a0 → tecrax-0.3.2a0}/scripts/validate_public_truth.py +23 -5
- tecrax-0.3.2a0/src/tecrax/__init__.py +16 -0
- {tecrax-0.2.2a0 → tecrax-0.3.2a0}/src/tecrax/cli.py +1 -1
- tecrax-0.3.2a0/src/tecrax/connectors/__init__.py +1 -0
- tecrax-0.3.2a0/src/tecrax/connectors/proxmox.py +90 -0
- tecrax-0.3.2a0/src/tecrax/connectors/proxmox_runtime.py +38 -0
- tecrax-0.3.2a0/src/tecrax/fixture/__init__.py +0 -0
- tecrax-0.3.2a0/src/tecrax/fixture/mock_runtime.py +67 -0
- tecrax-0.3.2a0/src/tecrax/internal_actions.py +75 -0
- tecrax-0.3.2a0/src/tecrax/profile/connectors/pbs.yaml +7 -0
- tecrax-0.3.2a0/src/tecrax/profile/connectors/proxmox.yaml +9 -0
- tecrax-0.3.2a0/src/tecrax/profile/intents/check_backup_status.yaml +7 -0
- tecrax-0.3.2a0/src/tecrax/profile/intents/restart_zabbix_agent.yaml +6 -0
- tecrax-0.3.2a0/src/tecrax/profile/profile.yaml +30 -0
- tecrax-0.3.2a0/src/tecrax/profile/validation_rules/check_backup_status.yaml +13 -0
- tecrax-0.3.2a0/src/tecrax/profile/validation_rules/restart_zabbix_agent.yaml +18 -0
- tecrax-0.3.2a0/src/tecrax/profile/workflows/check_backup_status.yaml +42 -0
- tecrax-0.3.2a0/src/tecrax/profile/workflows/restart_zabbix_agent.yaml +45 -0
- {tecrax-0.2.2a0 → tecrax-0.3.2a0}/tests/test_local_fixture.py +6 -3
- tecrax-0.3.2a0/tests/test_proxmox_connector_backend.py +32 -0
- tecrax-0.3.2a0/tests/test_proxmox_connector_templates.py +16 -0
- tecrax-0.3.2a0/tests/test_rexecop_profile.py +35 -0
- tecrax-0.2.2a0/PUBLIC_STATUS.md +0 -23
- tecrax-0.2.2a0/src/tecrax/__init__.py +0 -7
- {tecrax-0.2.2a0 → tecrax-0.3.2a0}/.gitignore +0 -0
- {tecrax-0.2.2a0 → tecrax-0.3.2a0}/LICENSE +0 -0
- {tecrax-0.2.2a0 → tecrax-0.3.2a0}/src/tecrax/local_fixture.py +0 -0
|
@@ -20,6 +20,10 @@ jobs:
|
|
|
20
20
|
python-version: ['3.11', '3.12']
|
|
21
21
|
steps:
|
|
22
22
|
- uses: actions/checkout@v6
|
|
23
|
+
- uses: actions/checkout@v6
|
|
24
|
+
with:
|
|
25
|
+
repository: rozmiarD/RExecOP
|
|
26
|
+
path: rexecop
|
|
23
27
|
- uses: actions/setup-python@v6
|
|
24
28
|
with:
|
|
25
29
|
python-version: ${{ matrix.python-version }}
|
|
@@ -28,6 +32,7 @@ jobs:
|
|
|
28
32
|
python -m pip install --upgrade pip
|
|
29
33
|
python -m pip install "sclite-core @ git+https://github.com/rozmiarD/SCLite.git@main"
|
|
30
34
|
python -m pip install "govengine @ git+https://github.com/rozmiarD/GovEngine.git@main"
|
|
35
|
+
python -m pip install -e ./rexecop
|
|
31
36
|
python -m pip install -e '.[dev]'
|
|
32
37
|
- name: Validate public truth
|
|
33
38
|
run: python scripts/validate_public_truth.py
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: tecrax
|
|
3
|
-
Version: 0.
|
|
4
|
-
Summary:
|
|
3
|
+
Version: 0.3.2a0
|
|
4
|
+
Summary: Governed infrastructure-operations profile and RExecOp domain package over GovEngine and SCLite.
|
|
5
5
|
Project-URL: Homepage, https://github.com/rozmiarD/tecrax
|
|
6
6
|
Project-URL: Repository, https://github.com/rozmiarD/tecrax
|
|
7
7
|
Author: Krzysztof Probola
|
|
@@ -17,22 +17,30 @@ Classifier: Programming Language :: Python :: 3.11
|
|
|
17
17
|
Classifier: Programming Language :: Python :: 3.12
|
|
18
18
|
Classifier: Topic :: System :: Systems Administration
|
|
19
19
|
Requires-Python: >=3.11
|
|
20
|
-
Requires-Dist: govengine<0.
|
|
21
|
-
Requires-Dist:
|
|
20
|
+
Requires-Dist: govengine<0.15,>=0.12.2a0
|
|
21
|
+
Requires-Dist: rexecop<0.3,>=0.2.2a0
|
|
22
|
+
Requires-Dist: sclite-core<1.1,>=1.0.1
|
|
22
23
|
Provides-Extra: dev
|
|
23
24
|
Requires-Dist: pytest<9,>=8; extra == 'dev'
|
|
25
|
+
Provides-Extra: fixture
|
|
26
|
+
Requires-Dist: rexecop<0.2,>=0.1.2a0; extra == 'fixture'
|
|
24
27
|
Description-Content-Type: text/markdown
|
|
25
28
|
|
|
26
29
|
# Tecrax
|
|
27
30
|
|
|
28
31
|
Tecrax is a governed infrastructure-operations runtime/profile built on GovEngine and SCLite.
|
|
29
32
|
|
|
30
|
-
Current
|
|
31
|
-
`govengine>=0.
|
|
33
|
+
Current published baseline: `tecrax==0.3.2a0`, depending on
|
|
34
|
+
`govengine>=0.12.2a0,<0.15`, `sclite-core>=1.0.1,<1.1`, and `rexecop>=0.2.2a0,<0.3`.
|
|
32
35
|
|
|
33
|
-
This
|
|
34
|
-
|
|
35
|
-
|
|
36
|
+
This package provides:
|
|
37
|
+
|
|
38
|
+
- **RExecOp domain profile** — bundled YAML profile with intents, workflows, connectors,
|
|
39
|
+
and validation rules (entry point `rexecop.profiles:tecrax`).
|
|
40
|
+
- **Local fixture review** — dry-run proof slice without live infrastructure.
|
|
41
|
+
|
|
42
|
+
It does not execute infrastructure changes, connect to hosts, manage credentials,
|
|
43
|
+
or provide production operational capability without explicit operator configuration.
|
|
36
44
|
|
|
37
45
|
Planned foundation:
|
|
38
46
|
|
|
@@ -45,7 +53,18 @@ Tecrax -> GovEngine -> SCLite
|
|
|
45
53
|
- Tecrax owns the infrastructure-operations profile semantics, fixture review
|
|
46
54
|
payloads, UX, and future host integrations when those boundaries are mature.
|
|
47
55
|
|
|
48
|
-
|
|
56
|
+
## RExecOp profile
|
|
57
|
+
|
|
58
|
+
Install `tecrax` alongside `rexecop` to register the domain profile:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
pip install rexecop tecrax
|
|
62
|
+
rexecop profile list
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
The profile root is exposed via `tecrax:profile_root` (directory `src/tecrax/profile/`).
|
|
66
|
+
|
|
67
|
+
## Local fixture proof
|
|
49
68
|
|
|
50
69
|
```bash
|
|
51
70
|
tecrax fixture-review --service demo-web
|
|
@@ -56,9 +75,9 @@ profile/planning/supervision/runtime-review contracts and binds its fixture
|
|
|
56
75
|
receipt through an SCLite artifact descriptor. It has no live runner, host
|
|
57
76
|
inventory, credential path, or infrastructure adapter.
|
|
58
77
|
|
|
59
|
-
The `0.
|
|
60
|
-
|
|
61
|
-
new contract surface.
|
|
78
|
+
The `0.3.1-alpha` release consolidates the RExecOp domain profile into this
|
|
79
|
+
package and aligns dependencies with RExecOp `0.1.x`. It does not add an
|
|
80
|
+
infrastructure runtime or a new contract surface beyond the bundled profile.
|
|
62
81
|
|
|
63
82
|
## Validation
|
|
64
83
|
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# Tecrax Public Status
|
|
2
|
+
|
|
3
|
+
- **Package version:** `0.3.2a0` (`0.3.1-alpha`)
|
|
4
|
+
- **Dependencies:** `govengine>=0.12.2a0,<0.15`, `sclite-core>=1.0.1,<1.1`, `rexecop>=0.2.2a0,<0.3`
|
|
5
|
+
- **RExecOp profile:** bundled at `src/tecrax/profile/` via `rexecop.profiles:tecrax`
|
|
6
|
+
- **Local fixture:** `tecrax fixture-review` — dry-run GovEngine/SCLite proof only
|
|
7
|
+
- **Not claimed:** live infrastructure runner, host inventory, credentials, carrier adapters, scheduler/storage, production readiness
|
|
@@ -2,12 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
Tecrax is a governed infrastructure-operations runtime/profile built on GovEngine and SCLite.
|
|
4
4
|
|
|
5
|
-
Current
|
|
6
|
-
`govengine>=0.
|
|
5
|
+
Current published baseline: `tecrax==0.3.2a0`, depending on
|
|
6
|
+
`govengine>=0.12.2a0,<0.15`, `sclite-core>=1.0.1,<1.1`, and `rexecop>=0.2.2a0,<0.3`.
|
|
7
7
|
|
|
8
|
-
This
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
This package provides:
|
|
9
|
+
|
|
10
|
+
- **RExecOp domain profile** — bundled YAML profile with intents, workflows, connectors,
|
|
11
|
+
and validation rules (entry point `rexecop.profiles:tecrax`).
|
|
12
|
+
- **Local fixture review** — dry-run proof slice without live infrastructure.
|
|
13
|
+
|
|
14
|
+
It does not execute infrastructure changes, connect to hosts, manage credentials,
|
|
15
|
+
or provide production operational capability without explicit operator configuration.
|
|
11
16
|
|
|
12
17
|
Planned foundation:
|
|
13
18
|
|
|
@@ -20,7 +25,18 @@ Tecrax -> GovEngine -> SCLite
|
|
|
20
25
|
- Tecrax owns the infrastructure-operations profile semantics, fixture review
|
|
21
26
|
payloads, UX, and future host integrations when those boundaries are mature.
|
|
22
27
|
|
|
23
|
-
|
|
28
|
+
## RExecOp profile
|
|
29
|
+
|
|
30
|
+
Install `tecrax` alongside `rexecop` to register the domain profile:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
pip install rexecop tecrax
|
|
34
|
+
rexecop profile list
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
The profile root is exposed via `tecrax:profile_root` (directory `src/tecrax/profile/`).
|
|
38
|
+
|
|
39
|
+
## Local fixture proof
|
|
24
40
|
|
|
25
41
|
```bash
|
|
26
42
|
tecrax fixture-review --service demo-web
|
|
@@ -31,9 +47,9 @@ profile/planning/supervision/runtime-review contracts and binds its fixture
|
|
|
31
47
|
receipt through an SCLite artifact descriptor. It has no live runner, host
|
|
32
48
|
inventory, credential path, or infrastructure adapter.
|
|
33
49
|
|
|
34
|
-
The `0.
|
|
35
|
-
|
|
36
|
-
new contract surface.
|
|
50
|
+
The `0.3.1-alpha` release consolidates the RExecOp domain profile into this
|
|
51
|
+
package and aligns dependencies with RExecOp `0.1.x`. It does not add an
|
|
52
|
+
infrastructure runtime or a new contract surface beyond the bundled profile.
|
|
37
53
|
|
|
38
54
|
## Validation
|
|
39
55
|
|
|
@@ -9,10 +9,12 @@ python -m pytest -q
|
|
|
9
9
|
tecrax fixture-review --service demo-web
|
|
10
10
|
```
|
|
11
11
|
|
|
12
|
-
Expected result for `0.
|
|
12
|
+
Expected result for `0.3.2a0`:
|
|
13
13
|
|
|
14
|
-
- `pyproject.toml`, `tecrax.__version__`, README, public status, and validators agree on `0.
|
|
15
|
-
-
|
|
14
|
+
- `pyproject.toml`, `tecrax.__version__`, README, public status, and validators agree on `0.3.2a0` / `0.3.1-alpha`;
|
|
15
|
+
- PyPI publication remains a fixture-only alpha package claim, not an infrastructure runtime claim;
|
|
16
|
+
- dependency truth is `govengine>=0.12.2a0,<0.15`, `sclite-core>=1.0.1,<1.1`, and `rexecop>=0.2.2a0,<0.3`;
|
|
17
|
+
- RExecOp profile entry point `tecrax:profile_root` resolves to a valid profile bundle;
|
|
16
18
|
- fixture review output validates GovEngine profile, planning, supervision, runtime snapshot, review result, and runtime contract proof objects;
|
|
17
19
|
- SCLite is used only for local artifact descriptors;
|
|
18
20
|
- non-claims remain explicit for live infrastructure, credentials, adapters, scheduler/storage, and production readiness.
|
|
@@ -4,8 +4,8 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "tecrax"
|
|
7
|
-
version = "0.
|
|
8
|
-
description = "
|
|
7
|
+
version = "0.3.2a0"
|
|
8
|
+
description = "Governed infrastructure-operations profile and RExecOp domain package over GovEngine and SCLite."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.11"
|
|
11
11
|
license = { text = "MIT" }
|
|
@@ -22,10 +22,21 @@ classifiers = [
|
|
|
22
22
|
"Topic :: System :: Systems Administration",
|
|
23
23
|
]
|
|
24
24
|
dependencies = [
|
|
25
|
-
"govengine>=0.
|
|
26
|
-
"sclite-core>=0.
|
|
25
|
+
"govengine>=0.12.2a0,<0.15",
|
|
26
|
+
"sclite-core>=1.0.1,<1.1",
|
|
27
|
+
"rexecop>=0.2.2a0,<0.3",
|
|
27
28
|
]
|
|
28
29
|
|
|
30
|
+
[project.entry-points."rexecop.profiles"]
|
|
31
|
+
tecrax = "tecrax:profile_root"
|
|
32
|
+
|
|
33
|
+
[project.entry-points."rexecop.internal_actions"]
|
|
34
|
+
tecrax = "tecrax.internal_actions:register_handlers"
|
|
35
|
+
|
|
36
|
+
[project.entry-points."rexecop.connector_backends"]
|
|
37
|
+
tecrax_fixture = "tecrax.fixture.mock_runtime:build_runtime"
|
|
38
|
+
tecrax_proxmox = "tecrax.connectors.proxmox_runtime:build_connector_runtime"
|
|
39
|
+
|
|
29
40
|
[project.urls]
|
|
30
41
|
Homepage = "https://github.com/rozmiarD/tecrax"
|
|
31
42
|
Repository = "https://github.com/rozmiarD/tecrax"
|
|
@@ -37,6 +48,9 @@ tecrax = "tecrax.cli:main"
|
|
|
37
48
|
dev = [
|
|
38
49
|
"pytest>=8,<9",
|
|
39
50
|
]
|
|
51
|
+
fixture = [
|
|
52
|
+
"rexecop>=0.1.2a0,<0.2",
|
|
53
|
+
]
|
|
40
54
|
|
|
41
55
|
[tool.hatch.build.targets.wheel]
|
|
42
56
|
packages = ["src/tecrax"]
|
|
@@ -14,10 +14,11 @@ import tecrax # noqa: E402
|
|
|
14
14
|
from tecrax.local_fixture import build_local_fixture_review # noqa: E402
|
|
15
15
|
|
|
16
16
|
|
|
17
|
-
EXPECTED_VERSION = '0.
|
|
18
|
-
EXPECTED_RELEASE_LABEL = '0.
|
|
19
|
-
EXPECTED_GOVENGINE = 'govengine>=0.
|
|
20
|
-
EXPECTED_SCLITE = 'sclite-core>=0.
|
|
17
|
+
EXPECTED_VERSION = '0.3.2a0'
|
|
18
|
+
EXPECTED_RELEASE_LABEL = '0.3.1-alpha'
|
|
19
|
+
EXPECTED_GOVENGINE = 'govengine>=0.12.2a0,<0.15'
|
|
20
|
+
EXPECTED_SCLITE = 'sclite-core>=1.0.1,<1.1'
|
|
21
|
+
EXPECTED_REXECOP = 'rexecop>=0.2.2a0,<0.3'
|
|
21
22
|
PUBLIC_DOCS = (
|
|
22
23
|
'README.md',
|
|
23
24
|
'PUBLIC_STATUS.md',
|
|
@@ -65,6 +66,7 @@ def collect_errors() -> list[str]:
|
|
|
65
66
|
version = str(project['version'])
|
|
66
67
|
govengine_dep = _dependency(project, 'govengine')
|
|
67
68
|
sclite_dep = _dependency(project, 'sclite-core')
|
|
69
|
+
rexecop_dep = _dependency(project, 'rexecop')
|
|
68
70
|
|
|
69
71
|
if project['name'] != 'tecrax':
|
|
70
72
|
errors.append(f'distribution_name_mismatch:{project["name"]}')
|
|
@@ -76,13 +78,22 @@ def collect_errors() -> list[str]:
|
|
|
76
78
|
errors.append(f'govengine_dependency_mismatch:{govengine_dep}!={EXPECTED_GOVENGINE}')
|
|
77
79
|
if sclite_dep != EXPECTED_SCLITE:
|
|
78
80
|
errors.append(f'sclite_dependency_mismatch:{sclite_dep}!={EXPECTED_SCLITE}')
|
|
81
|
+
if rexecop_dep != EXPECTED_REXECOP:
|
|
82
|
+
errors.append(f'rexecop_dependency_mismatch:{rexecop_dep}!={EXPECTED_REXECOP}')
|
|
79
83
|
|
|
80
84
|
for path in PUBLIC_DOCS:
|
|
81
85
|
_require(errors, path, EXPECTED_VERSION)
|
|
82
86
|
_require(errors, path, EXPECTED_GOVENGINE)
|
|
83
87
|
_require(errors, path, EXPECTED_SCLITE)
|
|
88
|
+
_require(errors, path, EXPECTED_REXECOP)
|
|
84
89
|
_require(errors, 'PUBLIC_STATUS.md', EXPECTED_RELEASE_LABEL)
|
|
85
90
|
_require(errors, 'README.md', 'tecrax fixture-review --service demo-web')
|
|
91
|
+
_require(errors, 'README.md', 'rexecop.profiles:tecrax')
|
|
92
|
+
_require(errors, 'pyproject.toml', 'rexecop.profiles')
|
|
93
|
+
_require(errors, 'pyproject.toml', 'tecrax:profile_root')
|
|
94
|
+
profile_root = Path(tecrax.profile_root())
|
|
95
|
+
if not (profile_root / 'profile.yaml').is_file():
|
|
96
|
+
errors.append('profile_bundle_missing:profile.yaml')
|
|
86
97
|
_require(errors, 'VALIDATION.md', 'python scripts/validate_public_truth.py')
|
|
87
98
|
_require(errors, '.github/workflows/ci.yml', 'actions/checkout@v6')
|
|
88
99
|
_require(errors, '.github/workflows/ci.yml', 'actions/setup-python@v6')
|
|
@@ -94,6 +105,8 @@ def collect_errors() -> list[str]:
|
|
|
94
105
|
_require(errors, '.github/workflows/ci.yml', 'python -m pip check')
|
|
95
106
|
_require(errors, '.github/workflows/ci.yml', 'sclite-core @ git+https://github.com/rozmiarD/SCLite.git@main')
|
|
96
107
|
_require(errors, '.github/workflows/ci.yml', 'govengine @ git+https://github.com/rozmiarD/GovEngine.git@main')
|
|
108
|
+
_require(errors, '.github/workflows/ci.yml', 'repository: rozmiarD/RExecOP')
|
|
109
|
+
_require(errors, '.github/workflows/ci.yml', 'pip install -e ./rexecop')
|
|
97
110
|
|
|
98
111
|
review = build_local_fixture_review('truth-fixture')
|
|
99
112
|
if review.get('artifact_type') != 'tecrax_local_fixture_review':
|
|
@@ -111,6 +124,11 @@ def collect_errors() -> list[str]:
|
|
|
111
124
|
errors.append('fixture_claims_credentials')
|
|
112
125
|
if not review.get('sclite_fixture_receipt_descriptor', {}).get('digest'):
|
|
113
126
|
errors.append('fixture_missing_sclite_descriptor_digest')
|
|
127
|
+
cli_text = _read('src/tecrax/cli.py')
|
|
128
|
+
if EXPECTED_RELEASE_LABEL not in cli_text:
|
|
129
|
+
errors.append(f'src/tecrax/cli.py:missing_current_release_label:{EXPECTED_RELEASE_LABEL}')
|
|
130
|
+
if '0.2.0-alpha' in cli_text or '0.2.1-alpha' in cli_text:
|
|
131
|
+
errors.append('src/tecrax/cli.py:stale_cli_status_release_label')
|
|
114
132
|
|
|
115
133
|
for path in PUBLIC_DOCS:
|
|
116
134
|
lowered = _read(path).lower()
|
|
@@ -128,7 +146,7 @@ def main() -> int:
|
|
|
128
146
|
for error in errors:
|
|
129
147
|
print(error, file=sys.stderr)
|
|
130
148
|
return 1
|
|
131
|
-
print(f'public_truth_ok:tecrax=={EXPECTED_VERSION}:{EXPECTED_GOVENGINE}:{EXPECTED_SCLITE}')
|
|
149
|
+
print(f'public_truth_ok:tecrax=={EXPECTED_VERSION}:{EXPECTED_GOVENGINE}:{EXPECTED_SCLITE}:{EXPECTED_REXECOP}')
|
|
132
150
|
return 0
|
|
133
151
|
|
|
134
152
|
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"""Tecrax governed infrastructure-operations profile and local-fixture runtime."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
from .local_fixture import build_local_fixture_review
|
|
8
|
+
|
|
9
|
+
__version__ = "0.3.2a0"
|
|
10
|
+
|
|
11
|
+
__all__ = ["__version__", "build_local_fixture_review", "profile_root"]
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def profile_root() -> str:
|
|
15
|
+
"""Entry point for rexecop.profiles — returns bundled RExecOp profile directory."""
|
|
16
|
+
return str(Path(__file__).resolve().parent / "profile")
|
|
@@ -28,7 +28,7 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
28
28
|
return 0
|
|
29
29
|
|
|
30
30
|
print(
|
|
31
|
-
"Tecrax 0.
|
|
31
|
+
"Tecrax 0.3.1-alpha local-fixture profile: no live infrastructure "
|
|
32
32
|
"connections, credentials, or operational changes are enabled."
|
|
33
33
|
)
|
|
34
34
|
return 0
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Domain connector helpers for Tecrax profiles."""
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def build_http_api_connector_config(
|
|
7
|
+
*,
|
|
8
|
+
staging_paths: bool = False,
|
|
9
|
+
base_url_secret_ref: str = "proxmox_base_url",
|
|
10
|
+
api_token_secret_ref: str = "proxmox_api_token",
|
|
11
|
+
timeout_seconds: int = 10,
|
|
12
|
+
max_retry_attempts: int = 2,
|
|
13
|
+
) -> dict[str, Any]:
|
|
14
|
+
"""Build environment YAML connector config for Proxmox over generic http_api."""
|
|
15
|
+
if staging_paths:
|
|
16
|
+
list_action: dict[str, Any] = {
|
|
17
|
+
"method": "GET",
|
|
18
|
+
"path": "/proxmox/vms/paged",
|
|
19
|
+
"pagination": {
|
|
20
|
+
"items_path": "data.vms",
|
|
21
|
+
"next_path": "data.next",
|
|
22
|
+
"max_pages": 5,
|
|
23
|
+
},
|
|
24
|
+
}
|
|
25
|
+
restart_path = "/proxmox/restart"
|
|
26
|
+
else:
|
|
27
|
+
list_action = {
|
|
28
|
+
"method": "GET",
|
|
29
|
+
"path": "/api2/json/cluster/resources",
|
|
30
|
+
"unwrap": "data",
|
|
31
|
+
}
|
|
32
|
+
restart_path = "/api2/json/nodes/{node}/qemu/{vmid}/status/restart"
|
|
33
|
+
|
|
34
|
+
actions: dict[str, Any] = {
|
|
35
|
+
"list_vms": list_action,
|
|
36
|
+
"restart": {
|
|
37
|
+
"method": "POST",
|
|
38
|
+
"path": restart_path,
|
|
39
|
+
"mutating": True,
|
|
40
|
+
"body": {"target": "{target}"},
|
|
41
|
+
},
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return {
|
|
45
|
+
"enabled": True,
|
|
46
|
+
"backend": "http_api",
|
|
47
|
+
"base_url_secret_ref": base_url_secret_ref,
|
|
48
|
+
"auth": {
|
|
49
|
+
"secret_ref": api_token_secret_ref,
|
|
50
|
+
"header": "Authorization",
|
|
51
|
+
"prefix": "PVEAPIToken=",
|
|
52
|
+
},
|
|
53
|
+
"timeout_seconds": timeout_seconds,
|
|
54
|
+
"retry": {
|
|
55
|
+
"max_attempts": max_retry_attempts,
|
|
56
|
+
"base_delay": 0.2,
|
|
57
|
+
"max_delay": 2.0,
|
|
58
|
+
"on": ["timeout", "transient_connector_error"],
|
|
59
|
+
},
|
|
60
|
+
"actions": actions,
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def merge_http_api_connector_config(
|
|
65
|
+
template: dict[str, Any],
|
|
66
|
+
overrides: dict[str, Any],
|
|
67
|
+
) -> dict[str, Any]:
|
|
68
|
+
"""Merge operator overrides into a Proxmox http_api template."""
|
|
69
|
+
merged: dict[str, Any] = dict(template)
|
|
70
|
+
skip_keys = {"backend", "plugin", "staging_paths"}
|
|
71
|
+
for key, value in overrides.items():
|
|
72
|
+
if key in skip_keys:
|
|
73
|
+
continue
|
|
74
|
+
if isinstance(value, dict) and isinstance(merged.get(key), dict):
|
|
75
|
+
nested = dict(merged[key])
|
|
76
|
+
nested.update(value)
|
|
77
|
+
merged[key] = nested
|
|
78
|
+
else:
|
|
79
|
+
merged[key] = value
|
|
80
|
+
merged["backend"] = "http_api"
|
|
81
|
+
if "base_url" in overrides:
|
|
82
|
+
merged.pop("base_url_secret_ref", None)
|
|
83
|
+
if "auth" not in overrides:
|
|
84
|
+
merged.pop("auth", None)
|
|
85
|
+
auth = overrides.get("auth")
|
|
86
|
+
if isinstance(auth, dict) and auth.get("secret_ref"):
|
|
87
|
+
merged_auth = dict(merged.get("auth") or {})
|
|
88
|
+
merged_auth.update(auth)
|
|
89
|
+
merged["auth"] = merged_auth
|
|
90
|
+
return merged
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
from rexecop.connectors.base import ConnectorRuntime
|
|
6
|
+
from rexecop.connectors.http_api import HttpApiConnectorRuntime
|
|
7
|
+
from rexecop.secrets.port import SecretResolver
|
|
8
|
+
from rexecop.secrets.resolver import default_secret_resolver
|
|
9
|
+
|
|
10
|
+
from tecrax.connectors.proxmox import build_http_api_connector_config, merge_http_api_connector_config
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def build_connector_runtime(
|
|
14
|
+
*,
|
|
15
|
+
connector_name: str,
|
|
16
|
+
config: dict[str, Any],
|
|
17
|
+
profile_root: str | None,
|
|
18
|
+
mutating_allowed: bool,
|
|
19
|
+
secret_resolver: SecretResolver | None = None,
|
|
20
|
+
) -> ConnectorRuntime:
|
|
21
|
+
"""Domain connector backend: Proxmox over generic http_api with Tecrax templates."""
|
|
22
|
+
template = build_http_api_connector_config(
|
|
23
|
+
staging_paths=bool(config.get("staging_paths")),
|
|
24
|
+
base_url_secret_ref=str(config.get("base_url_secret_ref") or "proxmox_base_url"),
|
|
25
|
+
api_token_secret_ref=str(config.get("api_token_secret_ref") or "proxmox_api_token"),
|
|
26
|
+
timeout_seconds=int(config.get("timeout_seconds") or 10),
|
|
27
|
+
max_retry_attempts=int((config.get("retry") or {}).get("max_attempts") or 2)
|
|
28
|
+
if isinstance(config.get("retry"), dict)
|
|
29
|
+
else int(config.get("max_retry_attempts") or 2),
|
|
30
|
+
)
|
|
31
|
+
merged = merge_http_api_connector_config(template, config)
|
|
32
|
+
return HttpApiConnectorRuntime(
|
|
33
|
+
connector_name=connector_name,
|
|
34
|
+
config=merged,
|
|
35
|
+
profile_root=profile_root,
|
|
36
|
+
mutating_allowed=mutating_allowed,
|
|
37
|
+
secret_resolver=secret_resolver or default_secret_resolver(),
|
|
38
|
+
)
|
|
File without changes
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from rexecop.connectors.base import ConnectorRequest, ConnectorResponse
|
|
4
|
+
from rexecop.connectors.mock_runtime import MockConnectorRuntime
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class TecraxFixtureConnectorRuntime(MockConnectorRuntime):
|
|
8
|
+
"""Domain fixture mock for Tecrax offline/bootstrap workflows."""
|
|
9
|
+
|
|
10
|
+
def invoke(self, request: ConnectorRequest) -> ConnectorResponse:
|
|
11
|
+
base = super().invoke(request)
|
|
12
|
+
if base.error and "unsupported mock" not in base.error:
|
|
13
|
+
return base
|
|
14
|
+
|
|
15
|
+
if request.connector == "proxmox" and request.action == "list_vms":
|
|
16
|
+
return ConnectorResponse(
|
|
17
|
+
connector=request.connector,
|
|
18
|
+
action=request.action,
|
|
19
|
+
success=True,
|
|
20
|
+
data={
|
|
21
|
+
"vms": [
|
|
22
|
+
{"id": "vm-101", "name": "zabbix-proxy", "critical": True},
|
|
23
|
+
{"id": "vm-102", "name": "backup-gateway", "critical": True},
|
|
24
|
+
]
|
|
25
|
+
},
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
if request.connector == "proxmox" and request.action == "restart":
|
|
29
|
+
before_state = {
|
|
30
|
+
"vm_id": "vm-101",
|
|
31
|
+
"agent_status": "running",
|
|
32
|
+
"target": request.target,
|
|
33
|
+
}
|
|
34
|
+
after_state = {
|
|
35
|
+
"vm_id": "vm-101",
|
|
36
|
+
"agent_status": "restarted",
|
|
37
|
+
"target": request.target,
|
|
38
|
+
}
|
|
39
|
+
return ConnectorResponse(
|
|
40
|
+
connector=request.connector,
|
|
41
|
+
action=request.action,
|
|
42
|
+
success=True,
|
|
43
|
+
data={
|
|
44
|
+
"before_state": before_state,
|
|
45
|
+
"after_state": after_state,
|
|
46
|
+
"mutation": "restart_zabbix_agent",
|
|
47
|
+
},
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
if request.connector == "pbs" and request.action == "list_snapshots":
|
|
51
|
+
return ConnectorResponse(
|
|
52
|
+
connector=request.connector,
|
|
53
|
+
action=request.action,
|
|
54
|
+
success=True,
|
|
55
|
+
data={
|
|
56
|
+
"snapshots": [
|
|
57
|
+
{"vm_id": "vm-101", "status": "ok"},
|
|
58
|
+
{"vm_id": "vm-102", "status": "ok"},
|
|
59
|
+
]
|
|
60
|
+
},
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
return base
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def build_runtime() -> TecraxFixtureConnectorRuntime:
|
|
67
|
+
return TecraxFixtureConnectorRuntime()
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from collections.abc import Mapping
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
from rexecop.execution.backend import StepExecutionContext
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def register_handlers() -> Mapping[str, Any]:
|
|
10
|
+
return {
|
|
11
|
+
"environment.resolve_targets": resolve_targets,
|
|
12
|
+
"correlate_vm_backup_coverage": correlate_vm_backup_coverage,
|
|
13
|
+
"capture_agent_state": capture_agent_state,
|
|
14
|
+
"verify_agent_state": verify_agent_state,
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def resolve_targets(context: StepExecutionContext) -> dict[str, Any]:
|
|
19
|
+
return {
|
|
20
|
+
"target": context.target,
|
|
21
|
+
"resolved_targets": ["vm-101", "vm-102"],
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def correlate_vm_backup_coverage(context: StepExecutionContext) -> dict[str, Any]:
|
|
26
|
+
connector_results = context.shared_state.get("connector_results", {})
|
|
27
|
+
vms: list[dict[str, Any]] = []
|
|
28
|
+
snapshots: list[dict[str, Any]] = []
|
|
29
|
+
for payload in connector_results.values():
|
|
30
|
+
if isinstance(payload, dict):
|
|
31
|
+
vms.extend(payload.get("vms", []))
|
|
32
|
+
snapshots.extend(payload.get("snapshots", []))
|
|
33
|
+
covered = {item.get("vm_id") for item in snapshots}
|
|
34
|
+
rows = []
|
|
35
|
+
for vm in vms:
|
|
36
|
+
vm_id = vm.get("id")
|
|
37
|
+
rows.append(
|
|
38
|
+
{
|
|
39
|
+
"vm_id": vm_id,
|
|
40
|
+
"name": vm.get("name"),
|
|
41
|
+
"backup_status": "ok" if vm_id in covered else "missing",
|
|
42
|
+
}
|
|
43
|
+
)
|
|
44
|
+
result = {
|
|
45
|
+
"rows": rows,
|
|
46
|
+
"all_critical_covered": all(row["backup_status"] == "ok" for row in rows),
|
|
47
|
+
}
|
|
48
|
+
context.shared_state["correlation"] = result
|
|
49
|
+
return result
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def capture_agent_state(context: StepExecutionContext) -> dict[str, Any]:
|
|
53
|
+
state = {
|
|
54
|
+
"target": context.target,
|
|
55
|
+
"agent_status": "running",
|
|
56
|
+
"vm_id": "vm-101",
|
|
57
|
+
}
|
|
58
|
+
context.shared_state["agent_before_state"] = dict(state)
|
|
59
|
+
return {"before_state": state}
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def verify_agent_state(context: StepExecutionContext) -> dict[str, Any]:
|
|
63
|
+
mutation = context.shared_state.get("mutation_states", {}).get("restart_agent", {})
|
|
64
|
+
after_state = mutation.get("after_state") if isinstance(mutation, dict) else None
|
|
65
|
+
if not isinstance(after_state, dict):
|
|
66
|
+
return {
|
|
67
|
+
"verified": False,
|
|
68
|
+
"reason": "missing restart mutation after_state",
|
|
69
|
+
}
|
|
70
|
+
context.shared_state["agent_after_state"] = dict(after_state)
|
|
71
|
+
return {
|
|
72
|
+
"verified": after_state.get("agent_status") == "restarted",
|
|
73
|
+
"after_state": after_state,
|
|
74
|
+
"before_state": context.shared_state.get("agent_before_state"),
|
|
75
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
profile_contract:
|
|
2
|
+
name: tecrax
|
|
3
|
+
version: "0.3.1"
|
|
4
|
+
|
|
5
|
+
intents:
|
|
6
|
+
required: true
|
|
7
|
+
|
|
8
|
+
workflows:
|
|
9
|
+
required: true
|
|
10
|
+
|
|
11
|
+
connector_requirements:
|
|
12
|
+
required: true
|
|
13
|
+
|
|
14
|
+
risk_classes:
|
|
15
|
+
required: true
|
|
16
|
+
|
|
17
|
+
evidence_requirements:
|
|
18
|
+
required: true
|
|
19
|
+
|
|
20
|
+
governance_expectations:
|
|
21
|
+
required: true
|
|
22
|
+
|
|
23
|
+
validation_rules:
|
|
24
|
+
required: true
|
|
25
|
+
|
|
26
|
+
rollback_rules:
|
|
27
|
+
optional: true
|
|
28
|
+
|
|
29
|
+
escalation_rules:
|
|
30
|
+
required: true
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
validation_rule:
|
|
2
|
+
intent: check_backup_status
|
|
3
|
+
steps:
|
|
4
|
+
- type: require_mapping
|
|
5
|
+
key: correlation
|
|
6
|
+
fail:
|
|
7
|
+
rule: check_backup_status.correlation_required
|
|
8
|
+
details:
|
|
9
|
+
reason: missing correlation result
|
|
10
|
+
- type: require_truthy_path
|
|
11
|
+
path: correlation.all_critical_covered
|
|
12
|
+
pass_rule: check_backup_status.all_critical_covered
|
|
13
|
+
details_from: correlation
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
validation_rule:
|
|
2
|
+
intent: restart_zabbix_agent
|
|
3
|
+
steps:
|
|
4
|
+
- type: require_mapping
|
|
5
|
+
key: agent_after_state
|
|
6
|
+
fail:
|
|
7
|
+
rule: restart_zabbix_agent.after_state_required
|
|
8
|
+
details:
|
|
9
|
+
reason: missing agent_after_state
|
|
10
|
+
- type: require_equals
|
|
11
|
+
path: agent_after_state.agent_status
|
|
12
|
+
value: restarted
|
|
13
|
+
pass_rule: restart_zabbix_agent.agent_restarted
|
|
14
|
+
details:
|
|
15
|
+
agent_after_state: $agent_after_state
|
|
16
|
+
mutation_states: $mutation_states.restart_agent
|
|
17
|
+
before_state: $mutation_states.restart_agent.before_state
|
|
18
|
+
after_state: $mutation_states.restart_agent.after_state
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
workflow:
|
|
2
|
+
id: tecrax.check_backup_status
|
|
3
|
+
intent: check_backup_status
|
|
4
|
+
mode: read_only
|
|
5
|
+
risk: low
|
|
6
|
+
description: Check backup coverage for critical VMs using profile-defined connectors.
|
|
7
|
+
|
|
8
|
+
steps:
|
|
9
|
+
- id: resolve_inventory
|
|
10
|
+
type: internal
|
|
11
|
+
action: environment.resolve_targets
|
|
12
|
+
pause_safe: true
|
|
13
|
+
|
|
14
|
+
- id: query_proxmox
|
|
15
|
+
type: connector
|
|
16
|
+
connector: proxmox
|
|
17
|
+
action: list_vms
|
|
18
|
+
timeout: 10s
|
|
19
|
+
pause_safe: false
|
|
20
|
+
|
|
21
|
+
- id: query_pbs
|
|
22
|
+
type: connector
|
|
23
|
+
connector: pbs
|
|
24
|
+
action: list_snapshots
|
|
25
|
+
timeout: 20s
|
|
26
|
+
pause_safe: false
|
|
27
|
+
|
|
28
|
+
- id: correlate
|
|
29
|
+
type: internal
|
|
30
|
+
action: correlate_vm_backup_coverage
|
|
31
|
+
pause_safe: true
|
|
32
|
+
|
|
33
|
+
- id: produce_receipt
|
|
34
|
+
type: evidence
|
|
35
|
+
action: produce_receipt
|
|
36
|
+
pause_safe: true
|
|
37
|
+
|
|
38
|
+
retry:
|
|
39
|
+
max_attempts: 2
|
|
40
|
+
allowed_on:
|
|
41
|
+
- timeout
|
|
42
|
+
- transient_connector_error
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
workflow:
|
|
2
|
+
id: tecrax.restart_zabbix_agent
|
|
3
|
+
intent: restart_zabbix_agent
|
|
4
|
+
mode: apply
|
|
5
|
+
risk: medium
|
|
6
|
+
description: Restart Zabbix agent on a target VM using mock Proxmox connector.
|
|
7
|
+
|
|
8
|
+
steps:
|
|
9
|
+
- id: capture_state
|
|
10
|
+
type: internal
|
|
11
|
+
action: capture_agent_state
|
|
12
|
+
pause_safe: true
|
|
13
|
+
|
|
14
|
+
- id: restart_agent
|
|
15
|
+
type: connector
|
|
16
|
+
connector: proxmox
|
|
17
|
+
action: restart
|
|
18
|
+
timeout: 30s
|
|
19
|
+
pause_safe: false
|
|
20
|
+
|
|
21
|
+
- id: verify_state
|
|
22
|
+
type: internal
|
|
23
|
+
action: verify_agent_state
|
|
24
|
+
pause_safe: true
|
|
25
|
+
|
|
26
|
+
- id: produce_receipt
|
|
27
|
+
type: evidence
|
|
28
|
+
action: produce_receipt
|
|
29
|
+
pause_safe: true
|
|
30
|
+
|
|
31
|
+
retry:
|
|
32
|
+
max_attempts: 2
|
|
33
|
+
allowed_on:
|
|
34
|
+
- timeout
|
|
35
|
+
- transient_connector_error
|
|
36
|
+
blocked_on:
|
|
37
|
+
- policy_denied
|
|
38
|
+
|
|
39
|
+
rollback:
|
|
40
|
+
mode: dry_run
|
|
41
|
+
steps:
|
|
42
|
+
- id: rollback_marker
|
|
43
|
+
type: internal
|
|
44
|
+
action: record_rollback_marker
|
|
45
|
+
pause_safe: true
|
|
@@ -29,12 +29,13 @@ def test_cli_status_keeps_local_fixture_posture(capsys) -> None:
|
|
|
29
29
|
assert main(['status']) == 0
|
|
30
30
|
|
|
31
31
|
stdout = capsys.readouterr().out
|
|
32
|
+
assert '0.3.1-alpha' in stdout
|
|
32
33
|
assert 'local-fixture profile' in stdout
|
|
33
34
|
assert 'no live infrastructure' in stdout
|
|
34
35
|
|
|
35
36
|
|
|
36
37
|
def test_version_and_public_truth_validator_agree() -> None:
|
|
37
|
-
assert __version__ == '0.
|
|
38
|
+
assert __version__ == '0.3.2a0'
|
|
38
39
|
result = subprocess.run(
|
|
39
40
|
[sys.executable, str(ROOT / 'scripts' / 'validate_public_truth.py')],
|
|
40
41
|
cwd=ROOT,
|
|
@@ -43,6 +44,8 @@ def test_version_and_public_truth_validator_agree() -> None:
|
|
|
43
44
|
check=True,
|
|
44
45
|
)
|
|
45
46
|
assert result.stdout.strip() == (
|
|
46
|
-
'public_truth_ok:tecrax==0.
|
|
47
|
-
'govengine>=0.
|
|
47
|
+
'public_truth_ok:tecrax==0.3.2a0:'
|
|
48
|
+
'govengine>=0.12.2a0,<0.15:'
|
|
49
|
+
'sclite-core>=1.0.1,<1.1:'
|
|
50
|
+
'rexecop>=0.2.2a0,<0.3'
|
|
48
51
|
)
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from importlib.metadata import entry_points
|
|
4
|
+
|
|
5
|
+
from tecrax.connectors.proxmox import build_http_api_connector_config, merge_http_api_connector_config
|
|
6
|
+
from tecrax.connectors.proxmox_runtime import build_connector_runtime
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def test_tecrax_proxmox_connector_backend_entry_point() -> None:
|
|
10
|
+
eps = entry_points(group="rexecop.connector_backends")
|
|
11
|
+
names = {ep.name for ep in eps}
|
|
12
|
+
assert "tecrax_proxmox" in names
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def test_merge_http_api_connector_config_drops_secret_ref_when_base_url_set() -> None:
|
|
16
|
+
template = build_http_api_connector_config(staging_paths=True)
|
|
17
|
+
merged = merge_http_api_connector_config(
|
|
18
|
+
template,
|
|
19
|
+
{"base_url": "http://example.test"},
|
|
20
|
+
)
|
|
21
|
+
assert merged["base_url"] == "http://example.test"
|
|
22
|
+
assert "base_url_secret_ref" not in merged
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def test_build_connector_runtime_returns_invokeable_backend() -> None:
|
|
26
|
+
runtime = build_connector_runtime(
|
|
27
|
+
connector_name="proxmox",
|
|
28
|
+
config={"staging_paths": True, "base_url": "http://127.0.0.1:9"},
|
|
29
|
+
profile_root=None,
|
|
30
|
+
mutating_allowed=False,
|
|
31
|
+
)
|
|
32
|
+
assert hasattr(runtime, "invoke")
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from tecrax.connectors.proxmox import build_http_api_connector_config
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def test_build_proxmox_http_api_config_staging_paths() -> None:
|
|
7
|
+
config = build_http_api_connector_config(staging_paths=True)
|
|
8
|
+
assert config["backend"] == "http_api"
|
|
9
|
+
assert config["actions"]["list_vms"]["pagination"]["items_path"] == "data.vms"
|
|
10
|
+
assert config["retry"]["base_delay"] == 0.2
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def test_build_proxmox_http_api_config_production_paths() -> None:
|
|
14
|
+
config = build_http_api_connector_config(staging_paths=False)
|
|
15
|
+
assert "/api2/json/cluster/resources" in config["actions"]["list_vms"]["path"]
|
|
16
|
+
assert config["actions"]["list_vms"]["unwrap"] == "data"
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from importlib.metadata import entry_points
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
import tecrax
|
|
7
|
+
from tecrax import profile_root
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def test_profile_root_points_to_bundled_directory() -> None:
|
|
11
|
+
root = Path(profile_root())
|
|
12
|
+
assert root.is_dir()
|
|
13
|
+
assert (root / "profile.yaml").is_file()
|
|
14
|
+
assert (root / "intents").is_dir()
|
|
15
|
+
assert (root / "workflows").is_dir()
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def test_profile_yaml_loads_with_expected_contract() -> None:
|
|
19
|
+
profile_path = Path(profile_root()) / "profile.yaml"
|
|
20
|
+
text = profile_path.read_text(encoding="utf-8")
|
|
21
|
+
assert "name: tecrax" in text
|
|
22
|
+
assert 'version: "0.3.1"' in text
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def test_rexecop_profiles_entry_point_registered() -> None:
|
|
26
|
+
eps = entry_points(group="rexecop.profiles")
|
|
27
|
+
names = {ep.name for ep in eps}
|
|
28
|
+
assert "tecrax" in names
|
|
29
|
+
tecrax_ep = next(ep for ep in eps if ep.name == "tecrax")
|
|
30
|
+
assert tecrax_ep.value == "tecrax:profile_root"
|
|
31
|
+
assert Path(tecrax_ep.load()()).is_dir()
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def test_package_version_matches_profile_bundle() -> None:
|
|
35
|
+
assert tecrax.__version__ == "0.3.2a0"
|
tecrax-0.2.2a0/PUBLIC_STATUS.md
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
# Tecrax Public Status
|
|
2
|
-
|
|
3
|
-
Tecrax is an **alpha local-fixture infrastructure-operations profile package** over GovEngine and SCLite.
|
|
4
|
-
|
|
5
|
-
## Current Truth
|
|
6
|
-
|
|
7
|
-
- Source version: `0.2.2a0`.
|
|
8
|
-
- Public release label: `0.2.2-alpha`.
|
|
9
|
-
- Dependency chain: `tecrax -> govengine>=0.11.0a0,<0.12 -> sclite-core>=0.8.0a0,<0.9`.
|
|
10
|
-
- Runtime posture: dry-run/local-fixture only.
|
|
11
|
-
- Public surface: `tecrax fixture-review --service demo-web`.
|
|
12
|
-
|
|
13
|
-
The `0.2.2-alpha` line is dependency/conformance synchronization only; it
|
|
14
|
-
keeps the fixture-only posture unchanged.
|
|
15
|
-
|
|
16
|
-
## Non-Claims
|
|
17
|
-
|
|
18
|
-
- no live infrastructure connection;
|
|
19
|
-
- no host inventory ownership;
|
|
20
|
-
- no credential, key-store, PKI, or KMS ownership;
|
|
21
|
-
- no carrier adapter implementation;
|
|
22
|
-
- no scheduler/storage/queue persistence ownership;
|
|
23
|
-
- no production operational readiness.
|
|
File without changes
|
|
File without changes
|
|
File without changes
|