request-vm-on-golem 0.1.52__tar.gz → 0.1.54__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.
- {request_vm_on_golem-0.1.52 → request_vm_on_golem-0.1.54}/PKG-INFO +7 -1
- {request_vm_on_golem-0.1.52 → request_vm_on_golem-0.1.54}/README.md +6 -0
- {request_vm_on_golem-0.1.52 → request_vm_on_golem-0.1.54}/pyproject.toml +1 -1
- {request_vm_on_golem-0.1.52 → request_vm_on_golem-0.1.54}/requestor/cli/commands.py +81 -2
- {request_vm_on_golem-0.1.52 → request_vm_on_golem-0.1.54}/requestor/config.py +3 -1
- {request_vm_on_golem-0.1.52 → request_vm_on_golem-0.1.54}/requestor/run.py +10 -0
- {request_vm_on_golem-0.1.52 → request_vm_on_golem-0.1.54}/requestor/utils/logging.py +15 -4
- {request_vm_on_golem-0.1.52 → request_vm_on_golem-0.1.54}/requestor/utils/spinner.py +12 -11
- {request_vm_on_golem-0.1.52 → request_vm_on_golem-0.1.54}/requestor/__init__.py +0 -0
- {request_vm_on_golem-0.1.52 → request_vm_on_golem-0.1.54}/requestor/api/main.py +0 -0
- {request_vm_on_golem-0.1.52 → request_vm_on_golem-0.1.54}/requestor/cli/__init__.py +0 -0
- {request_vm_on_golem-0.1.52 → request_vm_on_golem-0.1.54}/requestor/data/deployments/l2.json +0 -0
- {request_vm_on_golem-0.1.52 → request_vm_on_golem-0.1.54}/requestor/db/__init__.py +0 -0
- {request_vm_on_golem-0.1.52 → request_vm_on_golem-0.1.54}/requestor/db/sqlite.py +0 -0
- {request_vm_on_golem-0.1.52 → request_vm_on_golem-0.1.54}/requestor/errors.py +0 -0
- {request_vm_on_golem-0.1.52 → request_vm_on_golem-0.1.54}/requestor/payments/blockchain_service.py +0 -0
- {request_vm_on_golem-0.1.52 → request_vm_on_golem-0.1.54}/requestor/payments/monitor.py +0 -0
- {request_vm_on_golem-0.1.52 → request_vm_on_golem-0.1.54}/requestor/provider/__init__.py +0 -0
- {request_vm_on_golem-0.1.52 → request_vm_on_golem-0.1.54}/requestor/provider/client.py +0 -0
- {request_vm_on_golem-0.1.52 → request_vm_on_golem-0.1.54}/requestor/security/faucet.py +0 -0
- {request_vm_on_golem-0.1.52 → request_vm_on_golem-0.1.54}/requestor/services/__init__.py +0 -0
- {request_vm_on_golem-0.1.52 → request_vm_on_golem-0.1.54}/requestor/services/database_service.py +0 -0
- {request_vm_on_golem-0.1.52 → request_vm_on_golem-0.1.54}/requestor/services/provider_service.py +0 -0
- {request_vm_on_golem-0.1.52 → request_vm_on_golem-0.1.54}/requestor/services/ssh_service.py +0 -0
- {request_vm_on_golem-0.1.52 → request_vm_on_golem-0.1.54}/requestor/services/vm_service.py +0 -0
- {request_vm_on_golem-0.1.52 → request_vm_on_golem-0.1.54}/requestor/ssh/__init__.py +0 -0
- {request_vm_on_golem-0.1.52 → request_vm_on_golem-0.1.54}/requestor/ssh/manager.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.3
|
2
2
|
Name: request-vm-on-golem
|
3
|
-
Version: 0.1.
|
3
|
+
Version: 0.1.54
|
4
4
|
Summary: VM on Golem Requestor CLI - Create and manage virtual machines on the Golem Network
|
5
5
|
Keywords: golem,vm,cloud,decentralized,cli
|
6
6
|
Author: Phillip Jensen
|
@@ -69,6 +69,12 @@ golem vm create my-vm --provider-id 0xProvider --cpu 2 --memory 4 --storage 20
|
|
69
69
|
golem vm ssh my-vm
|
70
70
|
```
|
71
71
|
|
72
|
+
Check your installed version and whether an update is available:
|
73
|
+
|
74
|
+
```bash
|
75
|
+
golem version
|
76
|
+
```
|
77
|
+
|
72
78
|
5) Stop or destroy when done:
|
73
79
|
|
74
80
|
```bash
|
@@ -28,6 +28,12 @@ golem vm create my-vm --provider-id 0xProvider --cpu 2 --memory 4 --storage 20
|
|
28
28
|
golem vm ssh my-vm
|
29
29
|
```
|
30
30
|
|
31
|
+
Check your installed version and whether an update is available:
|
32
|
+
|
33
|
+
```bash
|
34
|
+
golem version
|
35
|
+
```
|
36
|
+
|
31
37
|
5) Stop or destroy when done:
|
32
38
|
|
33
39
|
```bash
|
@@ -1,6 +1,6 @@
|
|
1
1
|
[tool.poetry]
|
2
2
|
name = "request-vm-on-golem"
|
3
|
-
version = "0.1.
|
3
|
+
version = "0.1.54"
|
4
4
|
description = "VM on Golem Requestor CLI - Create and manage virtual machines on the Golem Network"
|
5
5
|
authors = ["Phillip Jensen <phillip+vm-on-golem@golemgrid.com>"]
|
6
6
|
readme = "README.md"
|
@@ -5,6 +5,7 @@ import json
|
|
5
5
|
from typing import Optional
|
6
6
|
from pathlib import Path
|
7
7
|
import subprocess
|
8
|
+
import os
|
8
9
|
import aiohttp
|
9
10
|
from tabulate import tabulate
|
10
11
|
import uvicorn
|
@@ -89,6 +90,33 @@ def cli(network: str | None):
|
|
89
90
|
pass
|
90
91
|
|
91
92
|
|
93
|
+
@cli.command(name="version")
|
94
|
+
def version_cmd():
|
95
|
+
"""Show installed and latest versions from PyPI."""
|
96
|
+
pkg = "request-vm-on-golem"
|
97
|
+
try:
|
98
|
+
current = metadata.version(pkg)
|
99
|
+
except Exception:
|
100
|
+
current = "unknown"
|
101
|
+
latest = None
|
102
|
+
# Avoid network during pytest
|
103
|
+
if not os.environ.get("PYTEST_CURRENT_TEST"):
|
104
|
+
try:
|
105
|
+
import json as _json
|
106
|
+
from urllib.request import urlopen
|
107
|
+
with urlopen(f"https://pypi.org/pypi/{pkg}/json", timeout=5) as resp:
|
108
|
+
data = _json.loads(resp.read().decode("utf-8"))
|
109
|
+
latest = data.get("info", {}).get("version")
|
110
|
+
except Exception:
|
111
|
+
latest = None
|
112
|
+
|
113
|
+
if latest and latest != current:
|
114
|
+
click.echo(f"Requestor CLI: {current} (update available: {latest})")
|
115
|
+
click.echo("Update: pip install -U request-vm-on-golem")
|
116
|
+
else:
|
117
|
+
click.echo(f"Requestor CLI: {current} (up-to-date)" if latest else f"Requestor CLI: {current}")
|
118
|
+
|
119
|
+
|
92
120
|
@cli.group()
|
93
121
|
def vm():
|
94
122
|
"""VM management commands"""
|
@@ -110,6 +138,8 @@ def vm():
|
|
110
138
|
async def list_providers(cpu: Optional[int], memory: Optional[int], storage: Optional[int], country: Optional[str], driver: Optional[str], payments_network: Optional[str] = None, all_payments: bool = False, as_json: bool = False, network: Optional[str] = None):
|
111
139
|
"""List available providers matching requirements."""
|
112
140
|
try:
|
141
|
+
if as_json:
|
142
|
+
os.environ["GOLEM_SILENCE_LOGS"] = "1"
|
113
143
|
if network:
|
114
144
|
config.network = network
|
115
145
|
# Log search criteria if any
|
@@ -153,7 +183,10 @@ async def list_providers(cpu: Optional[int], memory: Optional[int], storage: Opt
|
|
153
183
|
|
154
184
|
if not providers:
|
155
185
|
logger.warning("No providers found matching criteria")
|
156
|
-
|
186
|
+
result = {"providers": []}
|
187
|
+
if as_json:
|
188
|
+
click.echo(json.dumps(result, indent=2))
|
189
|
+
return result
|
157
190
|
|
158
191
|
# If JSON requested and full spec provided, include estimates per provider
|
159
192
|
if as_json and cpu and memory and storage:
|
@@ -188,6 +221,12 @@ async def list_providers(cpu: Optional[int], memory: Optional[int], storage: Opt
|
|
188
221
|
except Exception as e:
|
189
222
|
logger.error(f"Failed to list providers: {str(e)}")
|
190
223
|
raise click.Abort()
|
224
|
+
finally:
|
225
|
+
if as_json:
|
226
|
+
try:
|
227
|
+
del os.environ["GOLEM_SILENCE_LOGS"]
|
228
|
+
except Exception:
|
229
|
+
pass
|
191
230
|
|
192
231
|
|
193
232
|
@vm.command(name='create')
|
@@ -357,6 +396,8 @@ def vm_stream():
|
|
357
396
|
async def stream_list(as_json: bool):
|
358
397
|
"""List payment stream status for all known VMs."""
|
359
398
|
try:
|
399
|
+
if as_json:
|
400
|
+
os.environ["GOLEM_SILENCE_LOGS"] = "1"
|
360
401
|
vms = await db_service.list_vms()
|
361
402
|
if not vms:
|
362
403
|
logger.warning("No VMs found in local database")
|
@@ -433,6 +474,12 @@ async def stream_list(as_json: bool):
|
|
433
474
|
except Exception as e:
|
434
475
|
logger.error(f"Failed to list streams: {e}")
|
435
476
|
raise click.Abort()
|
477
|
+
finally:
|
478
|
+
if as_json:
|
479
|
+
try:
|
480
|
+
del os.environ["GOLEM_SILENCE_LOGS"]
|
481
|
+
except Exception:
|
482
|
+
pass
|
436
483
|
|
437
484
|
|
438
485
|
@vm_stream.command('open')
|
@@ -522,6 +569,8 @@ async def stream_topup(stream_id: int, glm: float | None, hours: int | None):
|
|
522
569
|
async def stream_status(name: str, as_json: bool):
|
523
570
|
"""Show the payment stream status for a VM by name."""
|
524
571
|
try:
|
572
|
+
if as_json:
|
573
|
+
os.environ["GOLEM_SILENCE_LOGS"] = "1"
|
525
574
|
# Resolve VM and provider
|
526
575
|
vm = await db_service.get_vm(name)
|
527
576
|
if not vm:
|
@@ -552,6 +601,12 @@ async def stream_status(name: str, as_json: bool):
|
|
552
601
|
except Exception as e:
|
553
602
|
logger.error(f"Failed to fetch stream status: {e}")
|
554
603
|
raise click.Abort()
|
604
|
+
finally:
|
605
|
+
if as_json:
|
606
|
+
try:
|
607
|
+
del os.environ["GOLEM_SILENCE_LOGS"]
|
608
|
+
except Exception:
|
609
|
+
pass
|
555
610
|
|
556
611
|
|
557
612
|
@vm_stream.command('inspect')
|
@@ -561,6 +616,8 @@ async def stream_status(name: str, as_json: bool):
|
|
561
616
|
async def stream_inspect(stream_id: int, as_json: bool):
|
562
617
|
"""Inspect a stream directly on-chain (no provider required)."""
|
563
618
|
try:
|
619
|
+
if as_json:
|
620
|
+
os.environ["GOLEM_SILENCE_LOGS"] = "1"
|
564
621
|
from web3 import Web3
|
565
622
|
from golem_streaming_abi import STREAM_PAYMENT_ABI
|
566
623
|
w3 = Web3(Web3.HTTPProvider(config.polygon_rpc_url))
|
@@ -604,6 +661,12 @@ async def stream_inspect(stream_id: int, as_json: bool):
|
|
604
661
|
except Exception as e:
|
605
662
|
logger.error(f"Failed to inspect stream: {e}")
|
606
663
|
raise click.Abort()
|
664
|
+
finally:
|
665
|
+
if as_json:
|
666
|
+
try:
|
667
|
+
del os.environ["GOLEM_SILENCE_LOGS"]
|
668
|
+
except Exception:
|
669
|
+
pass
|
607
670
|
|
608
671
|
|
609
672
|
@cli.group()
|
@@ -698,6 +761,8 @@ def connect_vm(name: str):
|
|
698
761
|
async def info_vm(name: str, as_json: bool):
|
699
762
|
"""Show information about a VM."""
|
700
763
|
try:
|
764
|
+
if as_json:
|
765
|
+
os.environ["GOLEM_SILENCE_LOGS"] = "1"
|
701
766
|
logger.command(f"ℹ️ Getting info for VM '{name}'")
|
702
767
|
|
703
768
|
# Initialize VM service
|
@@ -739,6 +804,12 @@ async def info_vm(name: str, as_json: bool):
|
|
739
804
|
except Exception as e:
|
740
805
|
logger.error(f"Failed to get VM info: {str(e)}")
|
741
806
|
raise click.Abort()
|
807
|
+
finally:
|
808
|
+
if as_json:
|
809
|
+
try:
|
810
|
+
del os.environ["GOLEM_SILENCE_LOGS"]
|
811
|
+
except Exception:
|
812
|
+
pass
|
742
813
|
|
743
814
|
|
744
815
|
@vm.command(name='destroy')
|
@@ -1026,6 +1097,8 @@ def run_api_server(host: str, port: int, reload: bool):
|
|
1026
1097
|
async def list_vms(as_json: bool):
|
1027
1098
|
"""List all VMs."""
|
1028
1099
|
try:
|
1100
|
+
if as_json:
|
1101
|
+
os.environ["GOLEM_SILENCE_LOGS"] = "1"
|
1029
1102
|
logger.command("📋 Listing your VMs")
|
1030
1103
|
logger.process("Fetching VM details")
|
1031
1104
|
|
@@ -1058,7 +1131,6 @@ async def list_vms(as_json: bool):
|
|
1058
1131
|
tablefmt="grid"
|
1059
1132
|
))
|
1060
1133
|
click.echo("\n" + "─" * 60)
|
1061
|
-
|
1062
1134
|
return result
|
1063
1135
|
|
1064
1136
|
except Exception as e:
|
@@ -1068,6 +1140,13 @@ async def list_vms(as_json: bool):
|
|
1068
1140
|
logger.error(f"Failed to list VMs: {error_msg}")
|
1069
1141
|
raise click.Abort()
|
1070
1142
|
|
1143
|
+
finally:
|
1144
|
+
if as_json:
|
1145
|
+
try:
|
1146
|
+
del os.environ["GOLEM_SILENCE_LOGS"]
|
1147
|
+
except Exception:
|
1148
|
+
pass
|
1149
|
+
|
1071
1150
|
|
1072
1151
|
def main():
|
1073
1152
|
"""Entry point for the CLI."""
|
@@ -4,6 +4,7 @@ import os
|
|
4
4
|
from pydantic_settings import BaseSettings, SettingsConfigDict
|
5
5
|
from pydantic import Field, field_validator, ValidationInfo
|
6
6
|
import os
|
7
|
+
import sys
|
7
8
|
|
8
9
|
|
9
10
|
def ensure_config() -> None:
|
@@ -33,7 +34,8 @@ def ensure_config() -> None:
|
|
33
34
|
created = True
|
34
35
|
|
35
36
|
if created:
|
36
|
-
|
37
|
+
# Write to stderr so stdout stays clean for JSON outputs
|
38
|
+
print("Using default settings – run with --help to customize", file=sys.stderr)
|
37
39
|
|
38
40
|
|
39
41
|
ensure_config()
|
@@ -4,6 +4,16 @@ import sys
|
|
4
4
|
from pathlib import Path
|
5
5
|
from dotenv import load_dotenv
|
6
6
|
|
7
|
+
if "--json" in sys.argv:
|
8
|
+
os.environ["GOLEM_SILENCE_LOGS"] = "1"
|
9
|
+
try:
|
10
|
+
import logging as _logging
|
11
|
+
_logging.getLogger().setLevel(_logging.CRITICAL)
|
12
|
+
_logging.getLogger('rlp').setLevel(_logging.CRITICAL)
|
13
|
+
_logging.getLogger('rlp.codec').setLevel(_logging.CRITICAL)
|
14
|
+
except Exception:
|
15
|
+
pass
|
16
|
+
|
7
17
|
from requestor.utils.logging import setup_logger
|
8
18
|
|
9
19
|
# Configure logging with debug mode from environment variable
|
@@ -61,6 +61,8 @@ def setup_logger(name: Optional[str] = None) -> logging.Logger:
|
|
61
61
|
|
62
62
|
# Check DEBUG environment variable
|
63
63
|
debug = os.getenv('DEBUG', '').lower() in ('1', 'true', 'yes')
|
64
|
+
# Global silence switch for JSON/machine outputs
|
65
|
+
silence = os.getenv('GOLEM_SILENCE_LOGS', '').lower() in ('1', 'true', 'yes')
|
64
66
|
|
65
67
|
# Prevent duplicate logs by removing root handlers
|
66
68
|
root = logging.getLogger()
|
@@ -86,13 +88,22 @@ def setup_logger(name: Optional[str] = None) -> logging.Logger:
|
|
86
88
|
style='%'
|
87
89
|
)
|
88
90
|
fancy_handler.setFormatter(fancy_formatter)
|
89
|
-
|
90
|
-
|
91
|
-
|
91
|
+
# Suppress DEBUG unless DEBUG=1; suppress everything if silence
|
92
|
+
def _filter(record: logging.LogRecord) -> bool:
|
93
|
+
if silence:
|
94
|
+
return False
|
95
|
+
return (record.levelno != DEBUG) or debug
|
96
|
+
fancy_handler.addFilter(_filter)
|
92
97
|
logger.addHandler(fancy_handler)
|
93
98
|
logger.propagate = False # Prevent propagation to avoid duplicates
|
94
99
|
|
95
|
-
if
|
100
|
+
if silence:
|
101
|
+
logger.setLevel(CRITICAL)
|
102
|
+
# Silence common libraries and root logger
|
103
|
+
logging.getLogger().setLevel(CRITICAL)
|
104
|
+
logging.getLogger('asyncio').setLevel(CRITICAL)
|
105
|
+
logging.getLogger('aiosqlite').setLevel(CRITICAL)
|
106
|
+
elif debug:
|
96
107
|
logger.setLevel(DEBUG)
|
97
108
|
# Enable debug logging for other libraries
|
98
109
|
logging.getLogger('asyncio').setLevel(DEBUG)
|
@@ -11,26 +11,27 @@ class Spinner:
|
|
11
11
|
self.busy = False
|
12
12
|
self.spinner_visible = False
|
13
13
|
self.message = message
|
14
|
-
|
14
|
+
# Use stderr so stdout can remain machine-readable (e.g., --json outputs)
|
15
|
+
sys.stderr.write('\033[?25l') # Hide cursor
|
15
16
|
|
16
17
|
def write_next(self):
|
17
18
|
"""Write the next spinner frame."""
|
18
19
|
with self._screen_lock:
|
19
20
|
if not self.spinner_visible:
|
20
|
-
sys.
|
21
|
+
sys.stderr.write(f"\r{next(self.spinner)} {self.message}")
|
21
22
|
self.spinner_visible = True
|
22
|
-
sys.
|
23
|
+
sys.stderr.flush()
|
23
24
|
|
24
25
|
def remove_spinner(self, cleanup=False):
|
25
26
|
"""Remove the spinner from the terminal."""
|
26
27
|
with self._screen_lock:
|
27
28
|
if self.spinner_visible:
|
28
|
-
sys.
|
29
|
-
sys.
|
30
|
-
sys.
|
29
|
+
sys.stderr.write('\r')
|
30
|
+
sys.stderr.write(' ' * (len(self.message) + 2))
|
31
|
+
sys.stderr.write('\r')
|
31
32
|
if cleanup:
|
32
|
-
sys.
|
33
|
-
sys.
|
33
|
+
sys.stderr.write('\033[?25h') # Show cursor
|
34
|
+
sys.stderr.flush()
|
34
35
|
self.spinner_visible = False
|
35
36
|
|
36
37
|
def spinner_task(self):
|
@@ -56,11 +57,11 @@ class Spinner:
|
|
56
57
|
self.remove_spinner(cleanup=True)
|
57
58
|
if exc_type is None:
|
58
59
|
# Show checkmark on success
|
59
|
-
sys.
|
60
|
+
sys.stderr.write(f"\r✓ {self.message}\n")
|
60
61
|
else:
|
61
62
|
# Show X on failure
|
62
|
-
sys.
|
63
|
-
sys.
|
63
|
+
sys.stderr.write(f"\r✗ {self.message}\n")
|
64
|
+
sys.stderr.flush()
|
64
65
|
|
65
66
|
def step(message):
|
66
67
|
"""Decorator to add a spinning progress indicator to a function."""
|
File without changes
|
File without changes
|
File without changes
|
{request_vm_on_golem-0.1.52 → request_vm_on_golem-0.1.54}/requestor/data/deployments/l2.json
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{request_vm_on_golem-0.1.52 → request_vm_on_golem-0.1.54}/requestor/payments/blockchain_service.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{request_vm_on_golem-0.1.52 → request_vm_on_golem-0.1.54}/requestor/services/database_service.py
RENAMED
File without changes
|
{request_vm_on_golem-0.1.52 → request_vm_on_golem-0.1.54}/requestor/services/provider_service.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|