manage-industry-dispatch.py 3.8 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879
  1. """Review existing collector request intents and settled history. No network or dispatch command."""
  2. import argparse
  3. import os
  4. import json
  5. import os
  6. from pathlib import Path
  7. import re
  8. import sys
  9. sys.path.insert(0, str(Path(__file__).resolve().parents[1] / 'server/voc-library'))
  10. from industry_monitor import load
  11. from monitoring_dispatch import batch_preflight, claim, import_closeout, preflight, projection, release
  12. from monitoring_store import need
  13. def json_arg(value):
  14. need(isinstance(value, str) and value, 'JSON_ARGUMENT_REQUIRED')
  15. return json.loads(Path(value[1:]).read_text('utf-8-sig') if value.startswith('@') else value)
  16. def main():
  17. p = argparse.ArgumentParser(description=__doc__)
  18. p.add_argument('command', choices=['status', 'preflight', 'batch', 'claim', 'release', 'import-closeout', 'import-ledger'])
  19. sources = p.add_mutually_exclusive_group(required=not bool(os.environ.get('KSF_UNIFIED_DB_CONFIG')))
  20. sources.add_argument('--snapshot'); sources.add_argument('--registry'); sources.add_argument('--db')
  21. p.add_argument('--generation', required=True)
  22. p.add_argument('--store', required=True)
  23. p.add_argument('--request', help='JSON or @file; batch expects an array')
  24. p.add_argument('--archive', type=Path)
  25. p.add_argument('--sha256')
  26. p.add_argument('--historical-archive', type=Path)
  27. p.add_argument('--historical-sha256')
  28. p.add_argument('--owner')
  29. p.add_argument('--lease', type=int, default=1800)
  30. p.add_argument('--lease-file', type=Path)
  31. p.add_argument('--out', type=Path)
  32. args = p.parse_args()
  33. config = {k: getattr(args, k) for k in ('snapshot', 'registry', 'db') if getattr(args, k)}
  34. data = load(config)
  35. need(data['generation'] == args.generation, 'LIBRARY_VERSION_CHANGED')
  36. if args.out:
  37. need(not args.out.exists(), 'OUTPUT_ALREADY_EXISTS')
  38. if args.command == 'import-ledger':
  39. from monitoring_ledger import register
  40. need(args.archive and args.sha256 and args.historical_archive and args.historical_sha256, 'LEDGER_ARCHIVES_AND_SHA_REQUIRED')
  41. result = register(args.store, args.archive, args.sha256, args.historical_archive, args.historical_sha256)
  42. elif args.command == 'import-closeout':
  43. need(args.archive and args.sha256, 'CLOSEOUT_ARCHIVE_AND_SHA_REQUIRED')
  44. result = import_closeout(args.store, args.archive, args.sha256)
  45. elif args.command == 'status':
  46. result = {'ok': True, **projection(args.store, args.generation)}
  47. elif args.command == 'preflight':
  48. result = preflight(args.store, json_arg(args.request), args.generation)
  49. elif args.command == 'batch':
  50. result = batch_preflight(args.store, json_arg(args.request), args.generation)
  51. elif args.command == 'claim':
  52. need(args.out is not None, 'PRIVATE_LEASE_OUTPUT_REQUIRED')
  53. result = claim(args.store, json_arg(args.request), args.generation, args.owner, args.lease)
  54. else:
  55. need(args.lease_file is not None, 'PRIVATE_LEASE_FILE_REQUIRED')
  56. result = release(args.store, json.loads(args.lease_file.read_text('utf-8-sig')), args.generation)
  57. if args.out:
  58. fd = os.open(args.out, os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o600)
  59. with os.fdopen(fd, 'w', encoding='utf-8') as stream:
  60. json.dump(result, stream, ensure_ascii=False, indent=2)
  61. stream.write('\n')
  62. # Token is restricted to the explicitly requested private lease file.
  63. public = {k: v for k, v in result.items() if k not in ('leaseToken', 'owner')}
  64. print(json.dumps(public, ensure_ascii=False))
  65. if __name__ == '__main__':
  66. try:
  67. main()
  68. except Exception as exc:
  69. message = str(exc)
  70. code = message if isinstance(exc, ValueError) and re.fullmatch('[A-Z][A-Z0-9_]+', message) else 'REQUEST_GUARD_FAILED'
  71. print(json.dumps({'ok': False, 'error': code, 'dispatchAllowed': False, 'paidCalls': 0}))
  72. sys.exit(1)