generated from coulomb/repo-seed
Wire ops inventory probes for Railiance
This commit is contained in:
@@ -15,6 +15,9 @@ data:
|
|||||||
ISSUE_CORE_URL: http://issue-core.issue-core.svc.cluster.local:8010
|
ISSUE_CORE_URL: http://issue-core.issue-core.svc.cluster.local:8010
|
||||||
ISSUE_SINK_TYPE: "null"
|
ISSUE_SINK_TYPE: "null"
|
||||||
ACTIVITY_DEFINITION_DIRS: /etc/activity-core/external-definitions
|
ACTIVITY_DEFINITION_DIRS: /etc/activity-core/external-definitions
|
||||||
|
OPS_INVENTORY_PATH: /etc/activity-core/ops/service-inventory.yml
|
||||||
|
INTER_HUB_URL: ""
|
||||||
|
OPS_HUB_WIDGET_MAPPING: ""
|
||||||
PROMETHEUS_BIND_ADDR: 0.0.0.0:9090
|
PROMETHEUS_BIND_ADDR: 0.0.0.0:9090
|
||||||
ACTIVITY_CURATOR_GATE: disabled
|
ACTIVITY_CURATOR_GATE: disabled
|
||||||
---
|
---
|
||||||
@@ -58,6 +61,219 @@ data:
|
|||||||
|
|
||||||
Kubernetes projection of the Custodian-owned definition in
|
Kubernetes projection of the Custodian-owned definition in
|
||||||
`/home/worsch/the-custodian/activity-definitions/hourly-recently-on-scope.md`.
|
`/home/worsch/the-custodian/activity-definitions/hourly-recently-on-scope.md`.
|
||||||
|
ops-service-inventory-probes.md: |
|
||||||
|
---
|
||||||
|
id: "40d15a87-7ff6-4d8e-992c-37df15f95110"
|
||||||
|
name: "Ops Service Inventory Probes"
|
||||||
|
type: activity-definition
|
||||||
|
version: "0.1"
|
||||||
|
enabled: false
|
||||||
|
owner: custodian
|
||||||
|
governance: custodian
|
||||||
|
status: proposed
|
||||||
|
created: "2026-06-05"
|
||||||
|
trigger:
|
||||||
|
type: cron
|
||||||
|
cron_expression: "15 * * * *"
|
||||||
|
timezone: Europe/Berlin
|
||||||
|
misfire_policy: skip
|
||||||
|
context_sources:
|
||||||
|
- type: ops-inventory
|
||||||
|
query: probe_services
|
||||||
|
required: false
|
||||||
|
params:
|
||||||
|
inventory_path: /etc/activity-core/ops/service-inventory.yml
|
||||||
|
timeout_seconds: 10
|
||||||
|
include_kinds:
|
||||||
|
- http
|
||||||
|
- https
|
||||||
|
allow_network: true
|
||||||
|
evidence_sinks:
|
||||||
|
- type: state-hub-progress
|
||||||
|
event_type: ops_inventory_probe
|
||||||
|
author: activity-core
|
||||||
|
bind_to: context.ops_inventory_probe
|
||||||
|
---
|
||||||
|
|
||||||
|
# ActivityDefinition: Ops Service Inventory Probes
|
||||||
|
|
||||||
|
Disabled Railiance projection of the Custodian-owned definition in
|
||||||
|
`/home/worsch/the-custodian/activity-definitions/ops-service-inventory-probes.md`.
|
||||||
|
Keep disabled until ops-hub Inter-Hub evidence intake is active.
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: actcore-ops-service-inventory
|
||||||
|
namespace: activity-core
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: activity-core
|
||||||
|
app.kubernetes.io/part-of: activity-core
|
||||||
|
data:
|
||||||
|
service-inventory.yml: |
|
||||||
|
version: 1
|
||||||
|
last_reviewed: "2026-06-05"
|
||||||
|
policy:
|
||||||
|
non_secret_inventory: true
|
||||||
|
source_of_truth: "/home/worsch/the-custodian/ops/service-inventory.yml"
|
||||||
|
projection: "Railiance activity-core ConfigMap snapshot for disabled probes"
|
||||||
|
environments:
|
||||||
|
- id: local
|
||||||
|
name: "Local Workstation"
|
||||||
|
role: "Workstation development and local operations"
|
||||||
|
lifecycle_state: observed
|
||||||
|
- id: coulombcore
|
||||||
|
name: "CoulombCore"
|
||||||
|
role: "Transitional production-like runtime"
|
||||||
|
lifecycle_state: observed
|
||||||
|
- id: railiance01
|
||||||
|
name: "Railiance01"
|
||||||
|
role: "First ThreePhoenix foundation node"
|
||||||
|
lifecycle_state: observed
|
||||||
|
- id: threephoenix-prod
|
||||||
|
name: "ThreePhoenix Production"
|
||||||
|
role: "Target governed production topology"
|
||||||
|
lifecycle_state: planned
|
||||||
|
hosts:
|
||||||
|
- id: local-workstation
|
||||||
|
environment: local
|
||||||
|
role: "State Hub and operator workstation runtime"
|
||||||
|
- id: coulombcore
|
||||||
|
environment: coulombcore
|
||||||
|
address: "92.205.130.254"
|
||||||
|
role: "Current live production-like server"
|
||||||
|
- id: railiance01
|
||||||
|
environment: railiance01
|
||||||
|
address: "92.205.62.239"
|
||||||
|
role: "First ThreePhoenix foundation node"
|
||||||
|
clusters:
|
||||||
|
- id: coulombcore-k3s
|
||||||
|
environment: coulombcore
|
||||||
|
host: coulombcore
|
||||||
|
kind: k3s
|
||||||
|
lifecycle_state: observed
|
||||||
|
- id: railiance01-k3s
|
||||||
|
environment: railiance01
|
||||||
|
host: railiance01
|
||||||
|
kind: k3s
|
||||||
|
lifecycle_state: observed
|
||||||
|
services:
|
||||||
|
- id: gitea
|
||||||
|
name: "Gitea"
|
||||||
|
kind: application
|
||||||
|
lifecycle_state: observed
|
||||||
|
health_status: unknown
|
||||||
|
environment: coulombcore
|
||||||
|
owner_repos:
|
||||||
|
- railiance-apps
|
||||||
|
runtime:
|
||||||
|
type: k3s
|
||||||
|
cluster: coulombcore-k3s
|
||||||
|
namespace: default
|
||||||
|
endpoints:
|
||||||
|
- id: gitea-oci-registry
|
||||||
|
type: https
|
||||||
|
url: "https://gitea.coulomb.social/v2/"
|
||||||
|
expected_status: 401
|
||||||
|
expected_signal: "OCI registry auth challenge"
|
||||||
|
widget_ref: "ops:endpoint:gitea-registry"
|
||||||
|
backing_stores:
|
||||||
|
- "database:gitea-db"
|
||||||
|
- "pvc:default/gitea-shared-storage"
|
||||||
|
access_paths:
|
||||||
|
- type: k8s
|
||||||
|
target: "coulombcore-k3s/default"
|
||||||
|
status: unknown
|
||||||
|
evidence: []
|
||||||
|
gaps:
|
||||||
|
- "Backup and restore evidence for database and shared storage not recorded in ops inventory."
|
||||||
|
- id: state-hub
|
||||||
|
name: "State Hub"
|
||||||
|
kind: coordination-service
|
||||||
|
lifecycle_state: observed
|
||||||
|
health_status: observed_ok
|
||||||
|
environment: local
|
||||||
|
owner_repos:
|
||||||
|
- state-hub
|
||||||
|
- the-custodian
|
||||||
|
runtime:
|
||||||
|
type: local-process
|
||||||
|
host: local-workstation
|
||||||
|
endpoints:
|
||||||
|
- id: state-hub-local-api
|
||||||
|
type: http
|
||||||
|
url: "http://actcore-state-hub-bridge:8000/state/health"
|
||||||
|
expected_status: 200
|
||||||
|
expected_signal: "health response"
|
||||||
|
backing_stores:
|
||||||
|
- "postgresql:state-hub"
|
||||||
|
access_paths:
|
||||||
|
- type: http
|
||||||
|
target: "http://actcore-state-hub-bridge:8000"
|
||||||
|
status: observed_ok
|
||||||
|
evidence: []
|
||||||
|
gaps:
|
||||||
|
- "Future cluster deployment readiness still needs ops evidence."
|
||||||
|
- id: inter-hub
|
||||||
|
name: "Inter-Hub"
|
||||||
|
kind: governance-service
|
||||||
|
lifecycle_state: observed
|
||||||
|
health_status: unknown
|
||||||
|
environment: threephoenix-prod
|
||||||
|
owner_repos:
|
||||||
|
- inter-hub
|
||||||
|
runtime:
|
||||||
|
type: external
|
||||||
|
public_endpoint: "https://hub.coulomb.social"
|
||||||
|
endpoints:
|
||||||
|
- id: inter-hub-openapi
|
||||||
|
type: https
|
||||||
|
url: "https://hub.coulomb.social/api/v2/openapi.json"
|
||||||
|
expected_status: 200
|
||||||
|
expected_signal: "OpenAPI document"
|
||||||
|
- id: inter-hub-ui
|
||||||
|
type: https
|
||||||
|
url: "https://hub.coulomb.social/Hubs"
|
||||||
|
expected_status: 302
|
||||||
|
expected_signal: "login redirect when unauthenticated"
|
||||||
|
backing_stores: []
|
||||||
|
access_paths:
|
||||||
|
- type: https
|
||||||
|
target: "https://hub.coulomb.social"
|
||||||
|
status: unknown
|
||||||
|
evidence: []
|
||||||
|
gaps:
|
||||||
|
- "ops-hub bootstrap requires authenticated UI flow or deployment-side migration."
|
||||||
|
- id: activity-core
|
||||||
|
name: "activity-core"
|
||||||
|
kind: automation-service
|
||||||
|
lifecycle_state: observed
|
||||||
|
health_status: observed_ok
|
||||||
|
environment: railiance01
|
||||||
|
owner_repos:
|
||||||
|
- activity-core
|
||||||
|
- the-custodian
|
||||||
|
runtime:
|
||||||
|
type: k3s
|
||||||
|
cluster: railiance01-k3s
|
||||||
|
namespace: activity-core
|
||||||
|
endpoints:
|
||||||
|
- id: activity-core-api
|
||||||
|
type: cluster-http
|
||||||
|
url: "http://actcore-api:8010/health"
|
||||||
|
expected_status: 200
|
||||||
|
expected_signal: "db"
|
||||||
|
backing_stores:
|
||||||
|
- "postgresql:activity-core"
|
||||||
|
- "temporal:activity-core"
|
||||||
|
- "nats:railiance01"
|
||||||
|
access_paths:
|
||||||
|
- type: k8s
|
||||||
|
target: "railiance01-k3s/activity-core"
|
||||||
|
status: observed_ok
|
||||||
|
evidence: []
|
||||||
|
gaps:
|
||||||
|
- "Add explicit ops inventory probes and evidence events."
|
||||||
---
|
---
|
||||||
apiVersion: v1
|
apiVersion: v1
|
||||||
kind: Service
|
kind: Service
|
||||||
@@ -360,10 +576,16 @@ spec:
|
|||||||
- name: external-activity-definitions
|
- name: external-activity-definitions
|
||||||
mountPath: /etc/activity-core/external-definitions/activity-definitions
|
mountPath: /etc/activity-core/external-definitions/activity-definitions
|
||||||
readOnly: true
|
readOnly: true
|
||||||
|
- name: ops-service-inventory
|
||||||
|
mountPath: /etc/activity-core/ops
|
||||||
|
readOnly: true
|
||||||
volumes:
|
volumes:
|
||||||
- name: external-activity-definitions
|
- name: external-activity-definitions
|
||||||
configMap:
|
configMap:
|
||||||
name: actcore-external-activity-definitions
|
name: actcore-external-activity-definitions
|
||||||
|
- name: ops-service-inventory
|
||||||
|
configMap:
|
||||||
|
name: actcore-ops-service-inventory
|
||||||
---
|
---
|
||||||
apiVersion: apps/v1
|
apiVersion: apps/v1
|
||||||
kind: Deployment
|
kind: Deployment
|
||||||
|
|||||||
@@ -16,6 +16,14 @@ name and access policy.
|
|||||||
The runtime image tag is `activity-core:railiance01-prod` and is expected to be
|
The runtime image tag is `activity-core:railiance01-prod` and is expected to be
|
||||||
loaded into the railiance01 K3s containerd image store.
|
loaded into the railiance01 K3s containerd image store.
|
||||||
|
|
||||||
|
`20-runtime.yaml` also projects the disabled Custodian-owned
|
||||||
|
`ops-service-inventory-probes.md` ActivityDefinition and a non-secret
|
||||||
|
`actcore-ops-service-inventory` ConfigMap snapshot. The source of truth for the
|
||||||
|
inventory remains `/home/worsch/the-custodian/ops/service-inventory.yml`; update
|
||||||
|
the ConfigMap projection from that file before enabling the probe schedule.
|
||||||
|
`OPS_HUB_KEY` is created only as an empty Secret placeholder until the operator
|
||||||
|
provisions the Inter-Hub ops-hub key.
|
||||||
|
|
||||||
## Deploy
|
## Deploy
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|||||||
@@ -36,5 +36,6 @@ if ! secret_exists actcore-runtime-secret; then
|
|||||||
kubectl -n "$NS" create secret generic actcore-runtime-secret \
|
kubectl -n "$NS" create secret generic actcore-runtime-secret \
|
||||||
--from-literal=ACTCORE_DB_URL="$ACTCORE_DB_URL" \
|
--from-literal=ACTCORE_DB_URL="$ACTCORE_DB_URL" \
|
||||||
--from-literal=WEBHOOK_SECRET_GITEA="" \
|
--from-literal=WEBHOOK_SECRET_GITEA="" \
|
||||||
--from-literal=WEBHOOK_SECRET_GITHUB=""
|
--from-literal=WEBHOOK_SECRET_GITHUB="" \
|
||||||
|
--from-literal=OPS_HUB_KEY=""
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -158,7 +158,11 @@ def _inter_hub_result(sink: dict[str, Any]) -> dict[str, Any]:
|
|||||||
missing.append("INTER_HUB_URL")
|
missing.append("INTER_HUB_URL")
|
||||||
if not os.environ.get("OPS_HUB_KEY"):
|
if not os.environ.get("OPS_HUB_KEY"):
|
||||||
missing.append("OPS_HUB_KEY")
|
missing.append("OPS_HUB_KEY")
|
||||||
if not (sink.get("widget_mapping") or sink.get("capability_mapping")):
|
if not (
|
||||||
|
sink.get("widget_mapping")
|
||||||
|
or sink.get("capability_mapping")
|
||||||
|
or os.environ.get("OPS_HUB_WIDGET_MAPPING")
|
||||||
|
):
|
||||||
missing.append("widget_mapping")
|
missing.append("widget_mapping")
|
||||||
|
|
||||||
if missing:
|
if missing:
|
||||||
|
|||||||
@@ -184,6 +184,25 @@ def test_inter_hub_sink_skips_cleanly_when_config_missing(monkeypatch) -> None:
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def test_inter_hub_sink_accepts_widget_mapping_from_env(monkeypatch) -> None:
|
||||||
|
monkeypatch.delenv("INTER_HUB_URL", raising=False)
|
||||||
|
monkeypatch.delenv("OPS_HUB_KEY", raising=False)
|
||||||
|
monkeypatch.setenv("OPS_HUB_WIDGET_MAPPING", "ops:endpoint:gitea-registry")
|
||||||
|
|
||||||
|
result = persist_ops_inventory_evidence(
|
||||||
|
_payload([{"type": "inter-hub-interaction-event"}])
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result == [
|
||||||
|
{
|
||||||
|
"type": "inter-hub-interaction-event",
|
||||||
|
"status": "skipped",
|
||||||
|
"reason": "missing_inter_hub_config",
|
||||||
|
"missing": ["INTER_HUB_URL", "OPS_HUB_KEY"],
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
def test_no_evidence_sinks_returns_no_results() -> None:
|
def test_no_evidence_sinks_returns_no_results() -> None:
|
||||||
payload = _payload([])
|
payload = _payload([])
|
||||||
payload["context_sources"][0]["params"] = {}
|
payload["context_sources"][0]["params"] = {}
|
||||||
|
|||||||
192
tests/test_railiance_ops_inventory_wiring.py
Normal file
192
tests/test_railiance_ops_inventory_wiring.py
Normal file
@@ -0,0 +1,192 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
import yaml
|
||||||
|
import httpx
|
||||||
|
|
||||||
|
from activity_core.definition_parser import parse_file
|
||||||
|
from activity_core.context_resolvers.ops_inventory import OpsInventoryContextResolver
|
||||||
|
from activity_core.ops_evidence_sinks import persist_ops_inventory_evidence
|
||||||
|
|
||||||
|
_REPO_ROOT = Path(__file__).parent.parent
|
||||||
|
_RUNTIME_PATH = _REPO_ROOT / "k8s" / "railiance" / "20-runtime.yaml"
|
||||||
|
_BOOTSTRAP_SECRETS_PATH = _REPO_ROOT / "k8s" / "railiance" / "bootstrap-secrets.sh"
|
||||||
|
|
||||||
|
|
||||||
|
def _resources() -> list[dict[str, Any]]:
|
||||||
|
return [
|
||||||
|
resource
|
||||||
|
for resource in yaml.safe_load_all(_RUNTIME_PATH.read_text(encoding="utf-8"))
|
||||||
|
if isinstance(resource, dict)
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def _by_kind_name(kind: str, name: str) -> dict[str, Any]:
|
||||||
|
for resource in _resources():
|
||||||
|
if resource.get("kind") == kind and resource.get("metadata", {}).get("name") == name:
|
||||||
|
return resource
|
||||||
|
raise AssertionError(f"missing {kind}/{name}")
|
||||||
|
|
||||||
|
|
||||||
|
def test_runtime_config_has_ops_inventory_placeholders() -> None:
|
||||||
|
config = _by_kind_name("ConfigMap", "actcore-runtime-config")
|
||||||
|
|
||||||
|
assert config["data"]["OPS_INVENTORY_PATH"] == (
|
||||||
|
"/etc/activity-core/ops/service-inventory.yml"
|
||||||
|
)
|
||||||
|
assert config["data"]["INTER_HUB_URL"] == ""
|
||||||
|
assert config["data"]["OPS_HUB_WIDGET_MAPPING"] == ""
|
||||||
|
|
||||||
|
|
||||||
|
def test_external_configmap_projects_disabled_ops_probe_definition(tmp_path) -> None:
|
||||||
|
config = _by_kind_name("ConfigMap", "actcore-external-activity-definitions")
|
||||||
|
raw_definition = config["data"]["ops-service-inventory-probes.md"]
|
||||||
|
definition_path = tmp_path / "ops-service-inventory-probes.md"
|
||||||
|
definition_path.write_text(raw_definition, encoding="utf-8")
|
||||||
|
|
||||||
|
definition = parse_file(definition_path)
|
||||||
|
|
||||||
|
assert definition.name == "Ops Service Inventory Probes"
|
||||||
|
assert definition.enabled is False
|
||||||
|
assert definition.trigger_config["cron_expression"] == "15 * * * *"
|
||||||
|
assert definition.context_sources == [
|
||||||
|
{
|
||||||
|
"type": "ops-inventory",
|
||||||
|
"query": "probe_services",
|
||||||
|
"required": False,
|
||||||
|
"params": {
|
||||||
|
"inventory_path": "/etc/activity-core/ops/service-inventory.yml",
|
||||||
|
"timeout_seconds": 10,
|
||||||
|
"include_kinds": ["http", "https"],
|
||||||
|
"allow_network": True,
|
||||||
|
"evidence_sinks": [
|
||||||
|
{
|
||||||
|
"type": "state-hub-progress",
|
||||||
|
"event_type": "ops_inventory_probe",
|
||||||
|
"author": "activity-core",
|
||||||
|
}
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"bind_to": "context.ops_inventory_probe",
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def test_ops_inventory_configmap_contains_probeable_inventory() -> None:
|
||||||
|
config = _by_kind_name("ConfigMap", "actcore-ops-service-inventory")
|
||||||
|
inventory = yaml.safe_load(config["data"]["service-inventory.yml"])
|
||||||
|
|
||||||
|
services = {service["id"]: service for service in inventory["services"]}
|
||||||
|
|
||||||
|
assert inventory["policy"]["non_secret_inventory"] is True
|
||||||
|
assert services["gitea"]["endpoints"][0]["id"] == "gitea-oci-registry"
|
||||||
|
assert services["state-hub"]["endpoints"][0]["url"] == (
|
||||||
|
"http://actcore-state-hub-bridge:8000/state/health"
|
||||||
|
)
|
||||||
|
assert services["inter-hub"]["endpoints"][0]["id"] == "inter-hub-openapi"
|
||||||
|
assert services["activity-core"]["endpoints"][0]["id"] == "activity-core-api"
|
||||||
|
|
||||||
|
|
||||||
|
def test_worker_mounts_ops_inventory_configmap() -> None:
|
||||||
|
deployment = _by_kind_name("Deployment", "actcore-worker")
|
||||||
|
pod_spec = deployment["spec"]["template"]["spec"]
|
||||||
|
container = pod_spec["containers"][0]
|
||||||
|
|
||||||
|
mounts = {mount["name"]: mount for mount in container["volumeMounts"]}
|
||||||
|
volumes = {volume["name"]: volume for volume in pod_spec["volumes"]}
|
||||||
|
|
||||||
|
assert mounts["ops-service-inventory"]["mountPath"] == "/etc/activity-core/ops"
|
||||||
|
assert mounts["ops-service-inventory"]["readOnly"] is True
|
||||||
|
assert volumes["ops-service-inventory"]["configMap"]["name"] == (
|
||||||
|
"actcore-ops-service-inventory"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_ops_hub_key_is_secret_only_placeholder() -> None:
|
||||||
|
runtime_config = _by_kind_name("ConfigMap", "actcore-runtime-config")
|
||||||
|
bootstrap = _BOOTSTRAP_SECRETS_PATH.read_text(encoding="utf-8")
|
||||||
|
|
||||||
|
assert "OPS_HUB_KEY" not in runtime_config["data"]
|
||||||
|
assert '--from-literal=OPS_HUB_KEY=""' in bootstrap
|
||||||
|
|
||||||
|
|
||||||
|
def test_disabled_ops_probe_definition_can_emit_fixture_evidence(
|
||||||
|
tmp_path,
|
||||||
|
monkeypatch,
|
||||||
|
) -> None:
|
||||||
|
definition_config = _by_kind_name("ConfigMap", "actcore-external-activity-definitions")
|
||||||
|
inventory_config = _by_kind_name("ConfigMap", "actcore-ops-service-inventory")
|
||||||
|
definition_path = tmp_path / "ops-service-inventory-probes.md"
|
||||||
|
inventory_path = tmp_path / "service-inventory.yml"
|
||||||
|
definition_path.write_text(
|
||||||
|
definition_config["data"]["ops-service-inventory-probes.md"],
|
||||||
|
encoding="utf-8",
|
||||||
|
)
|
||||||
|
inventory_path.write_text(
|
||||||
|
inventory_config["data"]["service-inventory.yml"],
|
||||||
|
encoding="utf-8",
|
||||||
|
)
|
||||||
|
definition = parse_file(definition_path)
|
||||||
|
source = definition.context_sources[0]
|
||||||
|
source["params"]["inventory_path"] = str(inventory_path)
|
||||||
|
|
||||||
|
def fake_endpoint_get(url: str, **kwargs: Any) -> Any:
|
||||||
|
if url.endswith("/v2/"):
|
||||||
|
return _HttpResponse(401, "OCI registry auth challenge")
|
||||||
|
if url.endswith("/state/health"):
|
||||||
|
return _HttpResponse(200, "health response")
|
||||||
|
if url.endswith("/openapi.json"):
|
||||||
|
return _HttpResponse(200, "OpenAPI document")
|
||||||
|
if url.endswith("/Hubs"):
|
||||||
|
return _HttpResponse(302, "login redirect when unauthenticated")
|
||||||
|
raise AssertionError(f"unexpected endpoint probe {url}")
|
||||||
|
|
||||||
|
monkeypatch.setattr(httpx, "get", fake_endpoint_get)
|
||||||
|
probe = OpsInventoryContextResolver().resolve("probe_services", None, source["params"])
|
||||||
|
|
||||||
|
posts: list[dict[str, Any]] = []
|
||||||
|
|
||||||
|
def fake_progress_get(url: str, **kwargs: Any) -> _JsonResponse:
|
||||||
|
return _JsonResponse([])
|
||||||
|
|
||||||
|
def fake_progress_post(url: str, **kwargs: Any) -> _JsonResponse:
|
||||||
|
posts.append({"url": url, **kwargs})
|
||||||
|
return _JsonResponse({"id": "progress-1"})
|
||||||
|
|
||||||
|
monkeypatch.setattr(httpx, "get", fake_progress_get)
|
||||||
|
monkeypatch.setattr(httpx, "post", fake_progress_post)
|
||||||
|
|
||||||
|
result = persist_ops_inventory_evidence(
|
||||||
|
{
|
||||||
|
"activity_id": definition.id,
|
||||||
|
"run_id": "12345678-aaaa-bbbb-cccc-123456789abc",
|
||||||
|
"scheduled_for": "2026-06-05T10:15:00+00:00",
|
||||||
|
"version_used": 1,
|
||||||
|
"context_sources": [source],
|
||||||
|
"context": {"ops_inventory_probe": probe},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
assert definition.enabled is False
|
||||||
|
assert result[0]["status"] == "posted"
|
||||||
|
assert posts[0]["json"]["event_type"] == "ops_inventory_probe"
|
||||||
|
assert posts[0]["json"]["detail"]["probe"]["summary"]["ok"] == 4
|
||||||
|
|
||||||
|
|
||||||
|
class _HttpResponse:
|
||||||
|
def __init__(self, status_code: int, text: str) -> None:
|
||||||
|
self.status_code = status_code
|
||||||
|
self.text = text
|
||||||
|
|
||||||
|
|
||||||
|
class _JsonResponse:
|
||||||
|
def __init__(self, payload: Any) -> None:
|
||||||
|
self.payload = payload
|
||||||
|
|
||||||
|
def raise_for_status(self) -> None:
|
||||||
|
return None
|
||||||
|
|
||||||
|
def json(self) -> Any:
|
||||||
|
return self.payload
|
||||||
@@ -159,7 +159,7 @@ valid and reviewable.
|
|||||||
|
|
||||||
```task
|
```task
|
||||||
id: ACTIVITY-WP-0007-T04
|
id: ACTIVITY-WP-0007-T04
|
||||||
status: progress
|
status: done
|
||||||
priority: medium
|
priority: medium
|
||||||
state_hub_task_id: "45132f9f-da3c-44f1-a488-195aa0e46428"
|
state_hub_task_id: "45132f9f-da3c-44f1-a488-195aa0e46428"
|
||||||
```
|
```
|
||||||
@@ -184,11 +184,17 @@ it prematurely.
|
|||||||
scan a disabled `ops-service-inventory-probes.md` definition carrying an
|
scan a disabled `ops-service-inventory-probes.md` definition carrying an
|
||||||
`ops-inventory` context source and explicit `state-hub-progress` evidence sink.
|
`ops-inventory` context source and explicit `state-hub-progress` evidence sink.
|
||||||
|
|
||||||
|
2026-06-05: Completed. The Railiance-projected disabled definition now uses the
|
||||||
|
`ops-inventory` resolver and explicit `state-hub-progress` evidence sink. Tests
|
||||||
|
prove the disabled definition can resolve fixture inventory data and emit one
|
||||||
|
compact `ops_inventory_probe` State Hub progress event without enabling the
|
||||||
|
production schedule.
|
||||||
|
|
||||||
## Wire Railiance Runtime Inputs
|
## Wire Railiance Runtime Inputs
|
||||||
|
|
||||||
```task
|
```task
|
||||||
id: ACTIVITY-WP-0007-T05
|
id: ACTIVITY-WP-0007-T05
|
||||||
status: todo
|
status: done
|
||||||
priority: medium
|
priority: medium
|
||||||
state_hub_task_id: "474564be-a447-4bdf-b995-168f7a93e515"
|
state_hub_task_id: "474564be-a447-4bdf-b995-168f7a93e515"
|
||||||
```
|
```
|
||||||
@@ -209,6 +215,13 @@ Scope:
|
|||||||
Done when the Railiance worker can see the disabled definition and inventory
|
Done when the Railiance worker can see the disabled definition and inventory
|
||||||
input without leaking secrets or activating the schedule early.
|
input without leaking secrets or activating the schedule early.
|
||||||
|
|
||||||
|
2026-06-05: Completed the first production wiring slice. `20-runtime.yaml`
|
||||||
|
projects the disabled ops probe definition, runtime config placeholders
|
||||||
|
(`OPS_INVENTORY_PATH`, `INTER_HUB_URL`, `OPS_HUB_WIDGET_MAPPING`), and a
|
||||||
|
non-secret `actcore-ops-service-inventory` ConfigMap snapshot. The worker mounts
|
||||||
|
the inventory at `/etc/activity-core/ops`, and `bootstrap-secrets.sh` keeps
|
||||||
|
`OPS_HUB_KEY` as an empty Secret-only placeholder until operator provisioning.
|
||||||
|
|
||||||
## Close Safety And Handoff Gates
|
## Close Safety And Handoff Gates
|
||||||
|
|
||||||
```task
|
```task
|
||||||
@@ -235,6 +248,10 @@ Acceptance criteria:
|
|||||||
This task waits on the implementation tasks above and, for final Inter-Hub
|
This task waits on the implementation tasks above and, for final Inter-Hub
|
||||||
activation, the operator-gated ops-hub widget/API-key path in `CUST-WP-0047`.
|
activation, the operator-gated ops-hub widget/API-key path in `CUST-WP-0047`.
|
||||||
|
|
||||||
|
2026-06-05: The local implementation gates are now satisfied and tested. Live
|
||||||
|
closure remains waiting on applying the updated Railiance manifests and on the
|
||||||
|
operator-gated Inter-Hub ops-hub widget/API-key path.
|
||||||
|
|
||||||
## Review Verdict
|
## Review Verdict
|
||||||
|
|
||||||
activity-core should provide this as a bounded probe-and-evidence capability.
|
activity-core should provide this as a bounded probe-and-evidence capability.
|
||||||
|
|||||||
Reference in New Issue
Block a user