fix: probe Angor IPv4 health endpoint inside the actual image

This commit is contained in:
archipelago
2026-09-30 16:40:16 -04:00
parent c82c1eee98
commit f992780957
5 changed files with 28 additions and 3 deletions
+2
View File
@@ -2,6 +2,8 @@
## Unreleased
- Fixed Angor Indexer health checks choosing IPv6 localhost for an IPv4 listener and unnecessarily restarting the working service.
- Prevented false app restarts by probing each published port at its actual bind address; Nginx Proxy Manager now checks its internal admin API.
- Added a backed-up migration for the recognized legacy Nginx Proxy Manager tunnel/LND port conflict in both OTA and ISO startup paths.
+1 -1
View File
@@ -48,7 +48,7 @@ app:
path: /
health_check:
type: http
endpoint: http://localhost:8080
endpoint: http://127.0.0.1:8080
path: /health
interval: 30s
timeout: 8s
+11
View File
@@ -302,3 +302,14 @@ ID/start time, every API probe returned success, and Bitcoin/LND/production-site
container IDs/start times were unchanged. This supersedes the initial short
restart-only acceptance recorded above. The generic backend fix is committed
for release, while the live node uses the equivalent internal NPM health check.
### Final-gate Angor health-check correction
Final release observation found the adapter healthy over IPv4 but marked
unhealthy by its in-container BusyBox wget: `localhost` resolved to `::1`, where
nginx does not listen. Its manifest now explicitly probes `127.0.0.1`. The live
managed service was refreshed and its real Podman health check passed. The
rootless gateway integration now runs the manifest's health check inside the
actual image, in addition to endpoint/security/outage/DNS recovery assertions;
all passed. A metadata regression covers the address-family requirement. Test
containers and their network were removed by the fixture cleanup.
+9 -2
View File
@@ -1,8 +1,12 @@
#!/usr/bin/env python3
"""Opt-in disposable rootless Angor gateway integration checks. No native app changes."""
import subprocess,pathlib,json,urllib.request,urllib.error,time,tempfile,os,uuid
import subprocess,pathlib,json,urllib.request,urllib.error,time,tempfile,os,uuid,shlex
import yaml
if os.environ.get('ARCHY_ALLOW_DISPOSABLE_CONTAINERS') != '1':
raise SystemExit('Set ARCHY_ALLOW_DISPOSABLE_CONTAINERS=1 to run isolated test containers')
manifest=yaml.safe_load((pathlib.Path(__file__).resolve().parents[2]/'apps/angor-indexer/manifest.yml').read_text())['app']
health=manifest['health_check']
health_url=health['endpoint'].rstrip('/')+health.get('path','/')
run_id=uuid.uuid4().hex[:12]
net='archy-angor-test-'+run_id;backend='angor-test-backend-'+run_id;gateway='angor-test-gateway-'+run_id
def run(*a):
@@ -28,8 +32,11 @@ assert subprocess.run(['podman','network','exists',net]).returncode==1
run('podman','network','create',net)
try:
start_backend()
run('podman','run','-d','--name',gateway,'--network',net,'--read-only','--cap-drop=all','--security-opt=no-new-privileges','--memory','128m','-p','127.0.0.1:19098:8080','source.archipelago-foundation.org/chaum/angor-indexer:1.0.1')
run('podman','run','-d','--name',gateway,'--network',net,'--read-only','--cap-drop=all','--security-opt=no-new-privileges','--memory','128m','--health-cmd','wget -q -T 5 -O /dev/null '+shlex.quote(health_url),'--health-interval','5s','--health-retries','2','-p','127.0.0.1:19098:8080','source.archipelago-foundation.org/chaum/angor-indexer:1.0.1')
ready()
run('podman','healthcheck','run',gateway)
assert json.loads(run('podman','inspect',gateway))[0]['State']['Health']['Status']=='healthy'
print('PASS manifest health check inside actual image (including localhost address family)',flush=True)
for path in ['/api/v1/address/bc1fixture/txs?after_txid=abc','/api/v1/fees/recommended','/api/tx/fixture/hex']:
status,headers,body=req(path,headers={'Cookie':'node-secret=do-not-forward','Authorization':'Bearer do-not-forward'})
result=json.loads(body);assert status==200 and result['url']==(path if path.startswith('/api/v1/') else path.replace('/api/','/api/v1/',1)) and result['cookie'] is None and result['auth'] is None
@@ -19,6 +19,11 @@ class ServiceMetadata(unittest.TestCase):
self.assertEqual(app['ports'][0]['bind'], '127.0.0.1')
self.assertEqual(app['security']['capabilities'], [])
def test_indexer_health_targets_ipv4_listener(self):
app = yaml.safe_load((ROOT / 'apps/angor-indexer/manifest.yml').read_text())['app']
self.assertEqual(app['health_check']['endpoint'], 'http://127.0.0.1:8080')
self.assertEqual(app['health_check']['path'], '/health')
def test_host_local_api_never_opens_mesh_port(self):
app = {'interfaces': {'main': {'type': 'api', 'port': 8999}},
'ports': [{'host': 8999, 'auth': 'local'}],