updates for testing
This commit is contained in:
@@ -65,3 +65,8 @@ Useful helper scripts:
|
||||
- [scripts/cloud/Invoke-LiveJiboPrep.ps1](/OpenJibo/scripts/cloud/Invoke-LiveJiboPrep.ps1)
|
||||
- [scripts/cloud/Get-WebSocketCaptureSummary.ps1](/OpenJibo/scripts/cloud/Get-WebSocketCaptureSummary.ps1)
|
||||
- [scripts/cloud/Import-WebSocketCaptureFixture.ps1](/OpenJibo/scripts/cloud/Import-WebSocketCaptureFixture.ps1)
|
||||
- [scripts/cloud/start-dotnet-with-node-cert.sh](/OpenJibo/scripts/cloud/start-dotnet-with-node-cert.sh)
|
||||
- [scripts/cloud/invoke-live-jibo-prep.sh](/OpenJibo/scripts/cloud/invoke-live-jibo-prep.sh)
|
||||
- [scripts/cloud/get-websocket-capture-summary.sh](/OpenJibo/scripts/cloud/get-websocket-capture-summary.sh)
|
||||
- [scripts/cloud/import-websocket-capture-fixture.py](/OpenJibo/scripts/cloud/import-websocket-capture-fixture.py)
|
||||
- [live-jibo-test-runbook.md](/OpenJibo/docs/live-jibo-test-runbook.md)
|
||||
|
||||
134
OpenJibo/docs/live-jibo-test-runbook.md
Normal file
134
OpenJibo/docs/live-jibo-test-runbook.md
Normal file
@@ -0,0 +1,134 @@
|
||||
# Live Jibo .NET Test Runbook
|
||||
|
||||
## Goal
|
||||
|
||||
Run the first real `Jibo -> .NET OpenJibo cloud` test on the Ubuntu machine using the same working certificate and controlled routing that currently work with the Node server.
|
||||
|
||||
This runbook intentionally avoids introducing Azure, new hostnames, or new robot bootstrap changes during the first live test.
|
||||
|
||||
## Recommended Approach
|
||||
|
||||
Use the existing Ubuntu networking path and certificate material first.
|
||||
|
||||
- keep the current controlled Wi-Fi / routing arrangement
|
||||
- keep the current Jibo-facing hostnames:
|
||||
- `api.jibo.com`
|
||||
- `api-socket.jibo.com`
|
||||
- `neo-hub.jibo.com`
|
||||
- keep the Node server available as a fallback
|
||||
- run the `.NET` API with the same cert/key material by converting it to a temporary `.pfx` for Kestrel
|
||||
|
||||
## Prerequisites On Ubuntu
|
||||
|
||||
Install or confirm these tools:
|
||||
|
||||
- `dotnet`
|
||||
- `openssl`
|
||||
- `curl`
|
||||
- `python3`
|
||||
|
||||
Optional but useful:
|
||||
|
||||
- `pwsh`
|
||||
|
||||
`pwsh` is not required anymore for the Ubuntu live test path if you use the bash/python helpers added here.
|
||||
|
||||
## Certificate Plan
|
||||
|
||||
The Node server currently uses:
|
||||
|
||||
- `cert.pem`
|
||||
- `key.pem`
|
||||
|
||||
The `.NET` API can reuse that same material for the test by converting it at startup into a temporary `.pfx`.
|
||||
|
||||
If your current cert file already includes the working chain, use it as-is.
|
||||
|
||||
If your chain is separate, pass it as `CHAIN_PEM`.
|
||||
|
||||
## Step By Step
|
||||
|
||||
1. On Ubuntu, stop the Node server if it is currently bound to port `443`.
|
||||
|
||||
2. From the repo root, start the `.NET` cloud using the same cert/key:
|
||||
|
||||
```bash
|
||||
./scripts/cloud/start-dotnet-with-node-cert.sh
|
||||
```
|
||||
|
||||
Optional environment overrides:
|
||||
|
||||
```bash
|
||||
CERT_PEM=/path/to/cert.pem \
|
||||
KEY_PEM=/path/to/key.pem \
|
||||
CHAIN_PEM=/path/to/chain.pem \
|
||||
ASPNETCORE_URLS="https://0.0.0.0:443;http://0.0.0.0:24605" \
|
||||
./scripts/cloud/start-dotnet-with-node-cert.sh
|
||||
```
|
||||
|
||||
3. In another terminal, run the prep checklist:
|
||||
|
||||
```bash
|
||||
./scripts/cloud/invoke-live-jibo-prep.sh
|
||||
```
|
||||
|
||||
4. Verify controlled routing from the Ubuntu environment:
|
||||
|
||||
```bash
|
||||
./scripts/bootstrap/test-openjibo-routing.sh
|
||||
```
|
||||
|
||||
5. Power on Jibo and let it connect using the existing controlled network configuration.
|
||||
|
||||
6. Perform the first live checks in this order:
|
||||
|
||||
- startup / bootstrap reachability
|
||||
- one simple chat turn
|
||||
- one joke turn
|
||||
|
||||
7. After the run, summarize the captured websocket telemetry:
|
||||
|
||||
```bash
|
||||
./scripts/cloud/get-websocket-capture-summary.sh
|
||||
```
|
||||
|
||||
8. Inspect exported fixtures under:
|
||||
|
||||
- `src/Jibo.Cloud/dotnet/src/Jibo.Cloud.Api/bin/Debug/net10.0/captures/websocket/fixtures/`
|
||||
|
||||
9. Import the best fixture into the checked-in websocket fixture set:
|
||||
|
||||
```bash
|
||||
python3 ./scripts/cloud/import-websocket-capture-fixture.py \
|
||||
/path/to/exported.flow.json \
|
||||
neo-hub-real-jibo-first-chat
|
||||
```
|
||||
|
||||
10. Keep notes on:
|
||||
|
||||
- whether startup succeeded cleanly
|
||||
- which websocket paths connected
|
||||
- whether audio stayed pending or finalized
|
||||
- whether EOS timing matched expectations
|
||||
- whether any unexpected message families appeared
|
||||
|
||||
## What To Do If The Test Fails
|
||||
|
||||
If the robot does not connect or the first turn fails:
|
||||
|
||||
1. confirm the `.NET` API is actually bound on `443`
|
||||
2. confirm the cert presented by the `.NET` API matches the currently working Node cert path
|
||||
3. confirm the Ubuntu routing still points Jibo traffic at the same machine
|
||||
4. compare the `.NET` websocket capture output with prior Node logs
|
||||
5. temporarily switch back to Node to confirm the environment still works
|
||||
|
||||
## Not In Scope For This First Test
|
||||
|
||||
Do not mix these into the first live run:
|
||||
|
||||
- Azure deployment cutover
|
||||
- new permanent OpenJibo hostnames
|
||||
- IaC rollout
|
||||
- new device bootstrap edits beyond the already working setup
|
||||
|
||||
Those are valid next steps, but they should follow the first successful `.NET` live capture, not precede it.
|
||||
16
OpenJibo/scripts/bootstrap/test-openjibo-routing.sh
Normal file
16
OpenJibo/scripts/bootstrap/test-openjibo-routing.sh
Normal file
@@ -0,0 +1,16 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
HOSTS=(
|
||||
"https://api.jibo.com/health"
|
||||
"https://api-socket.jibo.com/"
|
||||
"https://neo-hub.jibo.com/v1/proactive"
|
||||
)
|
||||
|
||||
for url in "${HOSTS[@]}"; do
|
||||
if status_code="$(curl --silent --output /dev/null --write-out "%{http_code}" --insecure "${url}")"; then
|
||||
echo "${url} status=${status_code} success=true"
|
||||
else
|
||||
echo "${url} status=000 success=false"
|
||||
fi
|
||||
done
|
||||
@@ -12,3 +12,11 @@ These scripts help exercise the new .NET hosted cloud locally.
|
||||
Runs a small readiness checklist before the first physical Jibo test against the .NET cloud.
|
||||
- `Import-WebSocketCaptureFixture.ps1`
|
||||
Sanitizes an exported websocket capture fixture and copies it into the checked-in websocket fixture set.
|
||||
- `start-dotnet-with-node-cert.sh`
|
||||
Starts the .NET API on Linux using the same PEM certificate material already used by the Node server.
|
||||
- `invoke-live-jibo-prep.sh`
|
||||
Bash equivalent of the live-run prep checklist for Ubuntu.
|
||||
- `get-websocket-capture-summary.sh`
|
||||
Bash summary helper for captured websocket telemetry and exported fixtures.
|
||||
- `import-websocket-capture-fixture.py`
|
||||
Cross-platform import/sanitization helper for exported websocket fixtures.
|
||||
|
||||
48
OpenJibo/scripts/cloud/get-websocket-capture-summary.sh
Normal file
48
OpenJibo/scripts/cloud/get-websocket-capture-summary.sh
Normal file
@@ -0,0 +1,48 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
CAPTURE_DIRECTORY="${1:-${SCRIPT_DIR}/../../src/Jibo.Cloud/dotnet/src/Jibo.Cloud.Api/bin/Debug/net10.0/captures/websocket}"
|
||||
|
||||
if [[ ! -d "${CAPTURE_DIRECTORY}" ]]; then
|
||||
echo "No websocket capture directory found at ${CAPTURE_DIRECTORY}"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
shopt -s nullglob
|
||||
event_files=( "${CAPTURE_DIRECTORY}"/*.events.ndjson )
|
||||
if [[ ${#event_files[@]} -eq 0 ]]; then
|
||||
echo "No websocket telemetry event files found in ${CAPTURE_DIRECTORY}"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
python3 - "$CAPTURE_DIRECTORY" "${event_files[@]}" <<'PY'
|
||||
import collections
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
|
||||
capture_dir = sys.argv[1]
|
||||
event_files = sys.argv[2:]
|
||||
|
||||
counter = collections.Counter()
|
||||
for path in event_files:
|
||||
with open(path, "r", encoding="utf-8") as handle:
|
||||
for line in handle:
|
||||
line = line.strip()
|
||||
if not line:
|
||||
continue
|
||||
payload = json.loads(line)
|
||||
counter[payload.get("EventType", "unknown")] += 1
|
||||
|
||||
for key in sorted(counter):
|
||||
print(f"{key}: {counter[key]}")
|
||||
|
||||
fixture_dir = os.path.join(capture_dir, "fixtures")
|
||||
if os.path.isdir(fixture_dir):
|
||||
print("")
|
||||
print("Exported websocket fixtures:")
|
||||
for name in sorted(os.listdir(fixture_dir)):
|
||||
if name.endswith(".flow.json"):
|
||||
print(f" - {name}")
|
||||
PY
|
||||
70
OpenJibo/scripts/cloud/import-websocket-capture-fixture.py
Normal file
70
OpenJibo/scripts/cloud/import-websocket-capture-fixture.py
Normal file
@@ -0,0 +1,70 @@
|
||||
#!/usr/bin/env python3
|
||||
import argparse
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def redact(value):
|
||||
if value is None:
|
||||
return None
|
||||
|
||||
if isinstance(value, str):
|
||||
lowered = value.lower()
|
||||
if "token" in lowered or "bearer" in lowered or "session" in lowered:
|
||||
return "[redacted]"
|
||||
return value
|
||||
|
||||
if isinstance(value, list):
|
||||
return [redact(item) for item in value]
|
||||
|
||||
if isinstance(value, dict):
|
||||
result = {}
|
||||
for key, item in value.items():
|
||||
lowered = key.lower()
|
||||
if "token" in lowered or "authorization" in lowered:
|
||||
result[key] = "[redacted]"
|
||||
else:
|
||||
result[key] = redact(item)
|
||||
return result
|
||||
|
||||
return value
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Import and sanitize an exported websocket capture fixture.")
|
||||
parser.add_argument("source_path")
|
||||
parser.add_argument("fixture_name")
|
||||
parser.add_argument(
|
||||
"--destination-directory",
|
||||
default=str(Path(__file__).resolve().parents[2] / "src" / "Jibo.Cloud" / "node" / "fixtures" / "websocket"),
|
||||
)
|
||||
parser.add_argument("--overwrite", action="store_true")
|
||||
args = parser.parse_args()
|
||||
|
||||
source_path = Path(args.source_path).resolve()
|
||||
destination_directory = Path(args.destination_directory).resolve()
|
||||
destination_directory.mkdir(parents=True, exist_ok=True)
|
||||
destination_path = destination_directory / f"{args.fixture_name}.flow.json"
|
||||
|
||||
if destination_path.exists() and not args.overwrite:
|
||||
raise SystemExit(f"Destination fixture already exists: {destination_path}. Use --overwrite to replace it.")
|
||||
|
||||
with source_path.open("r", encoding="utf-8") as handle:
|
||||
fixture = json.load(handle)
|
||||
|
||||
sanitized = redact(fixture)
|
||||
sanitized["name"] = args.fixture_name
|
||||
if "session" in sanitized and isinstance(sanitized["session"], dict):
|
||||
sanitized["session"]["token"] = "[redacted]"
|
||||
|
||||
with destination_path.open("w", encoding="utf-8", newline="\n") as handle:
|
||||
json.dump(sanitized, handle, indent=2)
|
||||
handle.write("\n")
|
||||
|
||||
print("Imported sanitized websocket fixture:")
|
||||
print(f" - source: {source_path}")
|
||||
print(f" - destination: {destination_path}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
37
OpenJibo/scripts/cloud/invoke-live-jibo-prep.sh
Normal file
37
OpenJibo/scripts/cloud/invoke-live-jibo-prep.sh
Normal file
@@ -0,0 +1,37 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
BASE_URL="${BASE_URL:-https://localhost:5001}"
|
||||
CAPTURE_DIRECTORY="${CAPTURE_DIRECTORY:-${SCRIPT_DIR}/../../src/Jibo.Cloud/dotnet/src/Jibo.Cloud.Api/bin/Debug/net10.0/captures/websocket}"
|
||||
EXPECTED_HOSTS=(
|
||||
"api.jibo.com"
|
||||
"api-socket.jibo.com"
|
||||
"neo-hub.jibo.com"
|
||||
)
|
||||
|
||||
echo "OpenJibo live Jibo prep"
|
||||
echo ""
|
||||
|
||||
echo "1. HTTP health check"
|
||||
curl --silent --show-error --fail "${BASE_URL%/}/health" | python3 -m json.tool
|
||||
|
||||
echo ""
|
||||
echo "2. Expected robot-facing hosts"
|
||||
for host in "${EXPECTED_HOSTS[@]}"; do
|
||||
echo " - ${host}"
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo "3. Capture directory"
|
||||
mkdir -p "${CAPTURE_DIRECTORY}"
|
||||
echo " - ${CAPTURE_DIRECTORY}"
|
||||
|
||||
echo ""
|
||||
echo "4. Live-run checklist"
|
||||
echo " - keep the Ubuntu/Jibo routing setup in place"
|
||||
echo " - keep the Node server available as a fallback"
|
||||
echo " - point Jibo at the .NET server using the same controlled network settings"
|
||||
echo " - perform one startup check, one chat turn, and one joke turn"
|
||||
echo " - after the run, inspect capture output with scripts/cloud/get-websocket-capture-summary.sh"
|
||||
echo " - import the best exported fixture with scripts/cloud/import-websocket-capture-fixture.py"
|
||||
67
OpenJibo/scripts/cloud/start-dotnet-with-node-cert.sh
Normal file
67
OpenJibo/scripts/cloud/start-dotnet-with-node-cert.sh
Normal file
@@ -0,0 +1,67 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
REPO_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)"
|
||||
API_PROJECT="${REPO_ROOT}/src/Jibo.Cloud/dotnet/src/Jibo.Cloud.Api/Jibo.Cloud.Api.csproj"
|
||||
|
||||
CERT_PEM="${CERT_PEM:-${REPO_ROOT}/src/Jibo.Cloud/node/cert.pem}"
|
||||
KEY_PEM="${KEY_PEM:-${REPO_ROOT}/src/Jibo.Cloud/node/key.pem}"
|
||||
CHAIN_PEM="${CHAIN_PEM:-}"
|
||||
PFX_OUT="${PFX_OUT:-${REPO_ROOT}/.tmp/openjibo-dev-cert.pfx}"
|
||||
PFX_PASSWORD="${PFX_PASSWORD:-openjibo-dev-password}"
|
||||
ASPNETCORE_URLS="${ASPNETCORE_URLS:-https://0.0.0.0:443;http://0.0.0.0:24605}"
|
||||
DOTNET_ENVIRONMENT="${DOTNET_ENVIRONMENT:-Development}"
|
||||
|
||||
mkdir -p "$(dirname "${PFX_OUT}")"
|
||||
|
||||
if [[ ! -f "${CERT_PEM}" ]]; then
|
||||
echo "Missing CERT_PEM: ${CERT_PEM}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ ! -f "${KEY_PEM}" ]]; then
|
||||
echo "Missing KEY_PEM: ${KEY_PEM}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
OPENSSL_ARGS=(
|
||||
pkcs12
|
||||
-export
|
||||
-out "${PFX_OUT}"
|
||||
-inkey "${KEY_PEM}"
|
||||
-in "${CERT_PEM}"
|
||||
-passout "pass:${PFX_PASSWORD}"
|
||||
)
|
||||
|
||||
if [[ -n "${CHAIN_PEM}" ]]; then
|
||||
if [[ ! -f "${CHAIN_PEM}" ]]; then
|
||||
echo "Missing CHAIN_PEM: ${CHAIN_PEM}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
OPENSSL_ARGS+=( -certfile "${CHAIN_PEM}" )
|
||||
fi
|
||||
|
||||
echo "Creating PFX for Kestrel"
|
||||
echo " - cert: ${CERT_PEM}"
|
||||
echo " - key: ${KEY_PEM}"
|
||||
if [[ -n "${CHAIN_PEM}" ]]; then
|
||||
echo " - chain: ${CHAIN_PEM}"
|
||||
fi
|
||||
echo " - pfx: ${PFX_OUT}"
|
||||
openssl "${OPENSSL_ARGS[@]}"
|
||||
|
||||
export ASPNETCORE_URLS
|
||||
export DOTNET_ENVIRONMENT
|
||||
export ASPNETCORE_Kestrel__Certificates__Default__Path="${PFX_OUT}"
|
||||
export ASPNETCORE_Kestrel__Certificates__Default__Password="${PFX_PASSWORD}"
|
||||
|
||||
echo ""
|
||||
echo "Starting OpenJibo .NET cloud"
|
||||
echo " - project: ${API_PROJECT}"
|
||||
echo " - urls: ${ASPNETCORE_URLS}"
|
||||
echo " - environment: ${DOTNET_ENVIRONMENT}"
|
||||
|
||||
cd "${REPO_ROOT}"
|
||||
exec dotnet run --project "${API_PROJECT}" --no-launch-profile
|
||||
Reference in New Issue
Block a user