golem-vm-provider 0.1.55__tar.gz → 0.1.57__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.
Files changed (44) hide show
  1. {golem_vm_provider-0.1.55 → golem_vm_provider-0.1.57}/PKG-INFO +74 -8
  2. {golem_vm_provider-0.1.55 → golem_vm_provider-0.1.57}/README.md +73 -7
  3. {golem_vm_provider-0.1.55 → golem_vm_provider-0.1.57}/provider/api/routes.py +14 -12
  4. {golem_vm_provider-0.1.55 → golem_vm_provider-0.1.57}/provider/config.py +2 -1
  5. {golem_vm_provider-0.1.55 → golem_vm_provider-0.1.57}/provider/container.py +2 -3
  6. {golem_vm_provider-0.1.55 → golem_vm_provider-0.1.57}/provider/main.py +646 -18
  7. {golem_vm_provider-0.1.55 → golem_vm_provider-0.1.57}/provider/network/port_verifier.py +32 -34
  8. {golem_vm_provider-0.1.55 → golem_vm_provider-0.1.57}/provider/utils/logging.py +18 -3
  9. {golem_vm_provider-0.1.55 → golem_vm_provider-0.1.57}/provider/utils/pricing.py +12 -1
  10. {golem_vm_provider-0.1.55 → golem_vm_provider-0.1.57}/pyproject.toml +1 -1
  11. {golem_vm_provider-0.1.55 → golem_vm_provider-0.1.57}/provider/__init__.py +0 -0
  12. {golem_vm_provider-0.1.55 → golem_vm_provider-0.1.57}/provider/api/__init__.py +0 -0
  13. {golem_vm_provider-0.1.55 → golem_vm_provider-0.1.57}/provider/api/models.py +0 -0
  14. {golem_vm_provider-0.1.55 → golem_vm_provider-0.1.57}/provider/data/deployments/l2.json +0 -0
  15. {golem_vm_provider-0.1.55 → golem_vm_provider-0.1.57}/provider/discovery/__init__.py +0 -0
  16. {golem_vm_provider-0.1.55 → golem_vm_provider-0.1.57}/provider/discovery/advertiser.py +0 -0
  17. {golem_vm_provider-0.1.55 → golem_vm_provider-0.1.57}/provider/discovery/golem_base_advertiser.py +0 -0
  18. {golem_vm_provider-0.1.55 → golem_vm_provider-0.1.57}/provider/discovery/golem_base_utils.py +0 -0
  19. {golem_vm_provider-0.1.55 → golem_vm_provider-0.1.57}/provider/discovery/multi_advertiser.py +0 -0
  20. {golem_vm_provider-0.1.55 → golem_vm_provider-0.1.57}/provider/discovery/resource_monitor.py +0 -0
  21. {golem_vm_provider-0.1.55 → golem_vm_provider-0.1.57}/provider/discovery/resource_tracker.py +0 -0
  22. {golem_vm_provider-0.1.55 → golem_vm_provider-0.1.57}/provider/discovery/service.py +0 -0
  23. {golem_vm_provider-0.1.55 → golem_vm_provider-0.1.57}/provider/payments/blockchain_service.py +0 -0
  24. {golem_vm_provider-0.1.55 → golem_vm_provider-0.1.57}/provider/payments/monitor.py +0 -0
  25. {golem_vm_provider-0.1.55 → golem_vm_provider-0.1.57}/provider/payments/stream_map.py +0 -0
  26. {golem_vm_provider-0.1.55 → golem_vm_provider-0.1.57}/provider/security/ethereum.py +0 -0
  27. {golem_vm_provider-0.1.55 → golem_vm_provider-0.1.57}/provider/security/faucet.py +0 -0
  28. {golem_vm_provider-0.1.55 → golem_vm_provider-0.1.57}/provider/security/l2_faucet.py +0 -0
  29. {golem_vm_provider-0.1.55 → golem_vm_provider-0.1.57}/provider/service.py +0 -0
  30. {golem_vm_provider-0.1.55 → golem_vm_provider-0.1.57}/provider/utils/__init__.py +0 -0
  31. {golem_vm_provider-0.1.55 → golem_vm_provider-0.1.57}/provider/utils/ascii_art.py +0 -0
  32. {golem_vm_provider-0.1.55 → golem_vm_provider-0.1.57}/provider/utils/port_display.py +0 -0
  33. {golem_vm_provider-0.1.55 → golem_vm_provider-0.1.57}/provider/utils/retry.py +0 -0
  34. {golem_vm_provider-0.1.55 → golem_vm_provider-0.1.57}/provider/utils/setup.py +0 -0
  35. {golem_vm_provider-0.1.55 → golem_vm_provider-0.1.57}/provider/vm/__init__.py +0 -0
  36. {golem_vm_provider-0.1.55 → golem_vm_provider-0.1.57}/provider/vm/cloud_init.py +0 -0
  37. {golem_vm_provider-0.1.55 → golem_vm_provider-0.1.57}/provider/vm/models.py +0 -0
  38. {golem_vm_provider-0.1.55 → golem_vm_provider-0.1.57}/provider/vm/multipass.py +0 -0
  39. {golem_vm_provider-0.1.55 → golem_vm_provider-0.1.57}/provider/vm/multipass_adapter.py +0 -0
  40. {golem_vm_provider-0.1.55 → golem_vm_provider-0.1.57}/provider/vm/name_mapper.py +0 -0
  41. {golem_vm_provider-0.1.55 → golem_vm_provider-0.1.57}/provider/vm/port_manager.py +0 -0
  42. {golem_vm_provider-0.1.55 → golem_vm_provider-0.1.57}/provider/vm/provider.py +0 -0
  43. {golem_vm_provider-0.1.55 → golem_vm_provider-0.1.57}/provider/vm/proxy_manager.py +0 -0
  44. {golem_vm_provider-0.1.55 → golem_vm_provider-0.1.57}/provider/vm/service.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: golem-vm-provider
3
- Version: 0.1.55
3
+ Version: 0.1.57
4
4
  Summary: VM on Golem Provider Node - Run your own provider node to offer VMs on the Golem Network
5
5
  Keywords: golem,vm,provider,cloud,decentralized
6
6
  Author: Phillip Jensen
@@ -61,6 +61,69 @@ pip install golem-vm-provider
61
61
  golem-provider start --network testnet
62
62
  ```
63
63
 
64
+ Verify your environment and connectivity anytime:
65
+
66
+ ```bash
67
+ golem-provider status
68
+ ```
69
+ This checks Multipass availability, local/external port reachability, and whether an update is available on PyPI.
70
+
71
+ ### Status Command (TTY and JSON)
72
+
73
+ Use `golem-provider status` to quickly assess health.
74
+
75
+ TTY output highlights
76
+
77
+ ```
78
+ Overall Error | Issues detected | Healthy
79
+
80
+ Multipass ✅ OK | ❌ Missing
81
+
82
+ Provider Port 0.0.0.0:7466
83
+ Local ✅ service is listening | ❌ port unavailable
84
+ External ✅ reachable | ❌ unreachable — <reason>
85
+
86
+ SSH Ports <start>-<end> — OK | limited — N issue(s) | blocked
87
+ Usable free <count> # free AND externally reachable
88
+ In use <count>
89
+ Issues e.g. "100 not reachable externally" or "3 unreachable, 1 not listening"
90
+ ```
91
+
92
+ Severity rules
93
+
94
+ - Overall is Error when any critical prerequisite fails:
95
+ - Provider API port not externally reachable (or external check fails).
96
+ - No externally reachable SSH ports in the configured range.
97
+ - Multipass missing or provider local port not ready.
98
+ - Otherwise it shows Issues detected or Healthy.
99
+
100
+ Machine‑readable JSON
101
+
102
+ ```bash
103
+ golem-provider status --json
104
+ ```
105
+
106
+ Key fields:
107
+
108
+ - `overall.status`: "healthy" | "issues" | "error"
109
+ - `overall.issues`: list of concise issue strings
110
+ - `ports.provider`:
111
+ - `port`: int, `host`: string
112
+ - `status`: "reachable" | "unreachable" (external check failures are treated as "unreachable")
113
+ - `ports.ssh`:
114
+ - `range`: [start, end)
115
+ - `status`: "ok" | "limited" | "blocked"
116
+ - `usable_free`: integer — free AND externally reachable
117
+ - `in_use`: integer
118
+ - `issues`: `{ unreachable: int, not_listening: int }`
119
+ - `ports`: array of per‑port summaries:
120
+ - `{ port: int, status: "reachable" | "unreachable" | "unknown", listening: bool }`
121
+
122
+ Notes
123
+
124
+ - The concept of "free" in JSON is replaced by `usable_free` (free + externally reachable) to avoid misleading counts when ports are blocked.
125
+ - When the external checker is unavailable, per‑port `status` is `"unknown"` and `listening` still reflects local state.
126
+
64
127
  3) Set pricing in USD (GLM rates auto‑compute):
65
128
 
66
129
  ```bash
@@ -592,13 +655,16 @@ The provider includes real-time port verification status:
592
655
 
593
656
  Example status output:
594
657
 
595
- ```bash
596
- 🌟 Port Verification Status
597
- ==========================
598
- [✅] Provider Port {provider_port}: External ✓ | Internal ✓
599
- [] VM Access Ports: 3 ports available ({start_port}-{start_port+2})
600
- [] Overall Status: Provider Ready
601
- └─ Can handle up to {n} concurrent VMs
658
+ ```
659
+ Overall Healthy
660
+
661
+ Provider Port {host}:{provider_port}
662
+ Local service is listening
663
+ External reachable
664
+
665
+ SSH Ports {start_port}-{end_port_minus_one} — OK
666
+ Usable free {usable_free}
667
+ In use {in_use}
602
668
  ```
603
669
 
604
670
  ### Resource Allocation Issues
@@ -16,6 +16,69 @@ pip install golem-vm-provider
16
16
  golem-provider start --network testnet
17
17
  ```
18
18
 
19
+ Verify your environment and connectivity anytime:
20
+
21
+ ```bash
22
+ golem-provider status
23
+ ```
24
+ This checks Multipass availability, local/external port reachability, and whether an update is available on PyPI.
25
+
26
+ ### Status Command (TTY and JSON)
27
+
28
+ Use `golem-provider status` to quickly assess health.
29
+
30
+ TTY output highlights
31
+
32
+ ```
33
+ Overall Error | Issues detected | Healthy
34
+
35
+ Multipass ✅ OK | ❌ Missing
36
+
37
+ Provider Port 0.0.0.0:7466
38
+ Local ✅ service is listening | ❌ port unavailable
39
+ External ✅ reachable | ❌ unreachable — <reason>
40
+
41
+ SSH Ports <start>-<end> — OK | limited — N issue(s) | blocked
42
+ Usable free <count> # free AND externally reachable
43
+ In use <count>
44
+ Issues e.g. "100 not reachable externally" or "3 unreachable, 1 not listening"
45
+ ```
46
+
47
+ Severity rules
48
+
49
+ - Overall is Error when any critical prerequisite fails:
50
+ - Provider API port not externally reachable (or external check fails).
51
+ - No externally reachable SSH ports in the configured range.
52
+ - Multipass missing or provider local port not ready.
53
+ - Otherwise it shows Issues detected or Healthy.
54
+
55
+ Machine‑readable JSON
56
+
57
+ ```bash
58
+ golem-provider status --json
59
+ ```
60
+
61
+ Key fields:
62
+
63
+ - `overall.status`: "healthy" | "issues" | "error"
64
+ - `overall.issues`: list of concise issue strings
65
+ - `ports.provider`:
66
+ - `port`: int, `host`: string
67
+ - `status`: "reachable" | "unreachable" (external check failures are treated as "unreachable")
68
+ - `ports.ssh`:
69
+ - `range`: [start, end)
70
+ - `status`: "ok" | "limited" | "blocked"
71
+ - `usable_free`: integer — free AND externally reachable
72
+ - `in_use`: integer
73
+ - `issues`: `{ unreachable: int, not_listening: int }`
74
+ - `ports`: array of per‑port summaries:
75
+ - `{ port: int, status: "reachable" | "unreachable" | "unknown", listening: bool }`
76
+
77
+ Notes
78
+
79
+ - The concept of "free" in JSON is replaced by `usable_free` (free + externally reachable) to avoid misleading counts when ports are blocked.
80
+ - When the external checker is unavailable, per‑port `status` is `"unknown"` and `listening` still reflects local state.
81
+
19
82
  3) Set pricing in USD (GLM rates auto‑compute):
20
83
 
21
84
  ```bash
@@ -547,13 +610,16 @@ The provider includes real-time port verification status:
547
610
 
548
611
  Example status output:
549
612
 
550
- ```bash
551
- 🌟 Port Verification Status
552
- ==========================
553
- [✅] Provider Port {provider_port}: External ✓ | Internal ✓
554
- [] VM Access Ports: 3 ports available ({start_port}-{start_port+2})
555
- [] Overall Status: Provider Ready
556
- └─ Can handle up to {n} concurrent VMs
613
+ ```
614
+ Overall Healthy
615
+
616
+ Provider Port {host}:{provider_port}
617
+ Local service is listening
618
+ External reachable
619
+
620
+ SSH Ports {start_port}-{end_port_minus_one} — OK
621
+ Usable free {usable_free}
622
+ In use {in_use}
557
623
  ```
558
624
 
559
625
  ### Resource Allocation Issues
@@ -7,8 +7,7 @@ from fastapi import APIRouter, HTTPException, Request
7
7
  from dependency_injector.wiring import inject, Provide
8
8
  from fastapi import APIRouter, HTTPException, Depends
9
9
 
10
- from ..config import Settings
11
- from ..config import Settings as _Cfg
10
+ from typing import TYPE_CHECKING, Any
12
11
  from ..container import Container
13
12
  from ..utils.logging import setup_logger
14
13
  from ..utils.ascii_art import vm_creation_animation, vm_status_change
@@ -27,7 +26,7 @@ router = APIRouter()
27
26
  async def create_vm(
28
27
  request: CreateVMRequest,
29
28
  vm_service: VMService = Depends(Provide[Container.vm_service]),
30
- settings: Settings = Depends(Provide[Container.config]),
29
+ settings: Any = Depends(Provide[Container.config]),
31
30
  stream_map = Depends(Provide[Container.stream_map]),
32
31
  ) -> VMInfo:
33
32
  """Create a new VM."""
@@ -39,11 +38,12 @@ async def create_vm(
39
38
  # If payments are enabled, require a valid stream before starting
40
39
  # Determine if we should enforce gating
41
40
  enforce = False
42
- spa = settings["STREAM_PAYMENT_ADDRESS"]
41
+ spa = (settings.get("STREAM_PAYMENT_ADDRESS") if isinstance(settings, dict) else getattr(settings, "STREAM_PAYMENT_ADDRESS", None))
43
42
  if spa and spa != "0x0000000000000000000000000000000000000000":
44
43
  if os.environ.get("PYTEST_CURRENT_TEST"):
45
44
  # In pytest, skip gating only when using default deployment address
46
45
  try:
46
+ from ..config import Settings as _Cfg # type: ignore
47
47
  default_spa, _ = _Cfg._load_l2_deployment() # type: ignore[attr-defined]
48
48
  except Exception:
49
49
  default_spa = None
@@ -54,8 +54,10 @@ async def create_vm(
54
54
  if enforce:
55
55
  if request.stream_id is None:
56
56
  raise HTTPException(status_code=400, detail="stream_id required when payments are enabled")
57
- reader = StreamPaymentReader(settings["POLYGON_RPC_URL"], settings["STREAM_PAYMENT_ADDRESS"])
58
- ok, reason = reader.verify_stream(int(request.stream_id), settings["PROVIDER_ID"])
57
+ rpc_url = settings.get("POLYGON_RPC_URL") if isinstance(settings, dict) else getattr(settings, "POLYGON_RPC_URL", None)
58
+ reader = StreamPaymentReader(rpc_url, spa)
59
+ expected_recipient = settings.get("PROVIDER_ID") if isinstance(settings, dict) else getattr(settings, "PROVIDER_ID", None)
60
+ ok, reason = reader.verify_stream(int(request.stream_id), expected_recipient)
59
61
  try:
60
62
  s = reader.get_stream(int(request.stream_id))
61
63
  now = int(reader.web3.eth.get_block("latest")["timestamp"]) # type: ignore[attr-defined]
@@ -73,7 +75,7 @@ async def create_vm(
73
75
  # Create VM config
74
76
  config = VMConfig(
75
77
  name=request.name,
76
- image=request.image or settings["DEFAULT_VM_IMAGE"],
78
+ image=request.image or (settings.get("DEFAULT_VM_IMAGE") if isinstance(settings, dict) else getattr(settings, "DEFAULT_VM_IMAGE", "")),
77
79
  resources=resources,
78
80
  ssh_key=request.ssh_key
79
81
  )
@@ -143,7 +145,7 @@ async def get_vm_status(
143
145
  async def get_vm_access(
144
146
  requestor_name: str,
145
147
  vm_service: VMService = Depends(Provide[Container.vm_service]),
146
- settings: Settings = Depends(Provide[Container.config]),
148
+ settings: Any = Depends(Provide[Container.config]),
147
149
  ) -> VMAccessInfo:
148
150
  """Get VM access information."""
149
151
  try:
@@ -156,7 +158,7 @@ async def get_vm_access(
156
158
  raise HTTPException(404, "VM mapping not found")
157
159
 
158
160
  return VMAccessInfo(
159
- ssh_host=settings["PUBLIC_IP"] or "localhost",
161
+ ssh_host=((settings.get("PUBLIC_IP") if isinstance(settings, dict) else getattr(settings, "PUBLIC_IP", None)) or "localhost"),
160
162
  ssh_port=vm.ssh_port,
161
163
  vm_id=requestor_name,
162
164
  multipass_name=multipass_name
@@ -222,7 +224,7 @@ async def delete_vm(
222
224
  raise HTTPException(status_code=500, detail="An unexpected error occurred")
223
225
  @router.get("/provider/info", response_model=ProviderInfoResponse)
224
226
  @inject
225
- async def provider_info(settings: Settings = Depends(Provide[Container.config])) -> ProviderInfoResponse:
227
+ async def provider_info(settings: Any = Depends(Provide[Container.config])) -> ProviderInfoResponse:
226
228
  return ProviderInfoResponse(
227
229
  provider_id=settings["PROVIDER_ID"],
228
230
  stream_payment_address=settings["STREAM_PAYMENT_ADDRESS"],
@@ -234,7 +236,7 @@ async def provider_info(settings: Settings = Depends(Provide[Container.config]))
234
236
  @inject
235
237
  async def get_vm_stream_status(
236
238
  requestor_name: str,
237
- settings: Settings = Depends(Provide[Container.config]),
239
+ settings: Any = Depends(Provide[Container.config]),
238
240
  stream_map = Depends(Provide[Container.stream_map]),
239
241
  ) -> StreamStatus:
240
242
  """Return on-chain stream status for a VM (if mapped)."""
@@ -266,7 +268,7 @@ async def get_vm_stream_status(
266
268
  @router.get("/payments/streams", response_model=List[StreamStatus])
267
269
  @inject
268
270
  async def list_stream_statuses(
269
- settings: Settings = Depends(Provide[Container.config]),
271
+ settings: Any = Depends(Provide[Container.config]),
270
272
  stream_map = Depends(Provide[Container.stream_map]),
271
273
  ) -> List[StreamStatus]:
272
274
  """List stream status for all mapped VMs."""
@@ -38,7 +38,8 @@ def ensure_config() -> None:
38
38
  created = True
39
39
 
40
40
  if created:
41
- print("Using default settings run with --help to customize")
41
+ # Inform the user, but write to stderr so JSON outputs on stdout remain clean
42
+ logger.info("Using default settings – run with --help to customize")
42
43
 
43
44
 
44
45
  if not os.environ.get("GOLEM_PROVIDER_SKIP_BOOTSTRAP") and not os.environ.get("PYTEST_CURRENT_TEST"):
@@ -2,7 +2,6 @@ import os
2
2
  from dependency_injector import containers, providers
3
3
  from pathlib import Path
4
4
 
5
- from .config import settings
6
5
  from .discovery.resource_tracker import ResourceTracker
7
6
  from .discovery.golem_base_advertiser import GolemBaseAdvertiser
8
7
  from .discovery.advertiser import DiscoveryServerAdvertiser
@@ -49,12 +48,12 @@ class Container(containers.DeclarativeContainer):
49
48
 
50
49
  vm_name_mapper = providers.Singleton(
51
50
  VMNameMapper,
52
- db_path=Path(settings.VM_DATA_DIR) / "vm_names.json",
51
+ db_path=providers.Callable(lambda base: Path(base) / "vm_names.json", config.VM_DATA_DIR),
53
52
  )
54
53
 
55
54
  stream_map = providers.Singleton(
56
55
  StreamMap,
57
- storage_path=Path(settings.VM_DATA_DIR) / "streams.json",
56
+ storage_path=providers.Callable(lambda base: Path(base) / "streams.json", config.VM_DATA_DIR),
58
57
  )
59
58
 
60
59
  port_manager = providers.Singleton(