golem-vm-provider 0.1.17__py3-none-any.whl → 0.1.19__py3-none-any.whl

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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: golem-vm-provider
3
- Version: 0.1.17
3
+ Version: 0.1.19
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
@@ -2,25 +2,26 @@ provider/__init__.py,sha256=HO1fkPpZqPO3z8O8-eVIyx8xXSMIVuTR_b1YF0RtXOg,45
2
2
  provider/api/__init__.py,sha256=ssX1ugDqEPt8Fn04IymgmG-Ev8PiXLsCSaiZVvHQnec,344
3
3
  provider/api/models.py,sha256=JOzoNf1oE5N97UqTN5xuIrTkqn2tCHqPDaIzGA3jUyo,3513
4
4
  provider/api/routes.py,sha256=P27RQvNqFWn6PacRwr1PaVz-yv5KAWsp9KeORejkXSI,6452
5
- provider/config.py,sha256=btSyU6cB6z8o9nHWpNEOBDxKqsEha1yStM-6of3Np0s,13237
5
+ provider/config.py,sha256=-Cu05ebOjUbhnh5iv3raQ7Z79HMhZ9EcRIRrZVW3Ino,14513
6
6
  provider/discovery/__init__.py,sha256=VR3NRoQtZRH5Vs8FG7jnGLR7p7wn7XeZdLaBb3t8e1g,123
7
7
  provider/discovery/advertiser.py,sha256=yv7RbRf1K43qOLAEa2Olj9hhN8etl2qsBuoHok0xoVs,6784
8
8
  provider/discovery/resource_tracker.py,sha256=8dYhJxoe_jLRwisHoA0jr575YhUKmLIqSXfW88KshcQ,6000
9
- provider/main.py,sha256=nVuMxq6npioif4-bFXGjQWJCKdy6O5ZbtX3zxhCM3zI,9206
9
+ provider/main.py,sha256=jl80WRIvsvfbHFvoPZqtIhFDUJm0andcyXqUu6Dfi2E,9389
10
10
  provider/network/port_verifier.py,sha256=AUtBGuZdfq9Jt4BRDuYesh5YEmwneEzYUgIw-uajZhA,12977
11
11
  provider/security/ethereum.py,sha256=SDRDbcjynbVy44kNnxlDcYLL0BZ3Qnc0DvmneQ-WKLE,1383
12
12
  provider/utils/ascii_art.py,sha256=ykBFsztk57GIiz1NJ-EII5UvN74iECqQL4h9VmiW6Z8,3161
13
13
  provider/utils/logging.py,sha256=C_elr0sJROHKQgErYpHJQvfujgh0k4Zf2gg8ZKfrmVk,2590
14
14
  provider/utils/port_display.py,sha256=5d_604Eo-82dqx_yV2ZScq7bKQ8IsXacc-yXC_KAz3A,11031
15
15
  provider/utils/retry.py,sha256=ekP2ucaSJNN-lBcrIvyHa4QYPKNITMl1a5V1X6BBvsw,1560
16
+ provider/utils/setup.py,sha256=Z5dLuBQkb5vdoQsu1HJZwXmu9NWsiBYJ7Vq9-C-_tY8,2932
16
17
  provider/vm/__init__.py,sha256=JGs50tUmzOR1rQ_w4fMY_3XWylmiA1G7KKWZkVw51mY,501
17
18
  provider/vm/cloud_init.py,sha256=E5dDH7dqStRcJNDfbarBBe83-c9N63W8B5ycIrHI8eU,4627
18
19
  provider/vm/models.py,sha256=zkfvP5Z50SPDNajwZTt9NTDIMRQIsZLvSOsuirHEcJM,6256
19
20
  provider/vm/multipass.py,sha256=FOcsfcJ-NrgBg_fvq_CKOKsQ0xOmk7Z34KXi3ag_Vl8,16603
20
21
  provider/vm/name_mapper.py,sha256=MrshNeJ4Dw-WBsyiIVcn9N5xyOxaBKX4Yqhyh_m5IFg,4103
21
22
  provider/vm/port_manager.py,sha256=d03uwU76vx6LgADMN8ffBT9t400XQ3vtYlXr6cLIFN0,9831
22
- provider/vm/proxy_manager.py,sha256=cu0FPPbeCc3CR6NRE_CnLjiRg7xVdSFUylVUOL1g1sI,10154
23
- golem_vm_provider-0.1.17.dist-info/METADATA,sha256=Lu9EpHlgFmOQP56l89vLyowyM0j48TI5BTGfT_EQxtQ,10594
24
- golem_vm_provider-0.1.17.dist-info/WHEEL,sha256=XbeZDeTWKc1w7CSIyre5aMDU_-PohRwTQceYnisIYYY,88
25
- golem_vm_provider-0.1.17.dist-info/entry_points.txt,sha256=E4rCWo_Do_2zCG_GewNuftfVlHF_8b_OvioZre0dfeA,54
26
- golem_vm_provider-0.1.17.dist-info/RECORD,,
23
+ provider/vm/proxy_manager.py,sha256=k12bjq1WkizkpUJIyFEoHgT21vhy0l2pSggp3m-8bFc,10895
24
+ golem_vm_provider-0.1.19.dist-info/METADATA,sha256=zt40xePOqxAmby_p-Keplksm4vnBXJ8k3H5G4H6FEqs,10594
25
+ golem_vm_provider-0.1.19.dist-info/WHEEL,sha256=XbeZDeTWKc1w7CSIyre5aMDU_-PohRwTQceYnisIYYY,88
26
+ golem_vm_provider-0.1.19.dist-info/entry_points.txt,sha256=E4rCWo_Do_2zCG_GewNuftfVlHF_8b_OvioZre0dfeA,54
27
+ golem_vm_provider-0.1.19.dist-info/RECORD,,
provider/config.py CHANGED
@@ -64,6 +64,7 @@ class Settings(BaseSettings):
64
64
  """Resolve and create cloud-init directory path."""
65
65
  import platform
66
66
  import tempfile
67
+ from .utils.setup import setup_cloud_init_dir, check_setup_needed, mark_setup_complete
67
68
 
68
69
  def verify_dir_permissions(path: Path) -> bool:
69
70
  """Verify directory has correct permissions and is accessible."""
@@ -86,15 +87,32 @@ class Settings(BaseSettings):
86
87
  if system == "linux" and Path("/snap/bin/multipass").exists():
87
88
  # Linux with snap
88
89
  path = Path("/var/snap/multipass/common/cloud-init")
90
+
91
+ # Check if we need to set up permissions
92
+ if check_setup_needed():
93
+ logger.info("First run detected, setting up cloud-init directory...")
94
+ success, error = setup_cloud_init_dir(path)
95
+ if success:
96
+ logger.info("✓ Cloud-init directory setup complete")
97
+ mark_setup_complete()
98
+ else:
99
+ logger.error(f"Failed to set up cloud-init directory: {error}")
100
+ logger.error("\nTo fix this manually, run these commands:")
101
+ logger.error(" sudo mkdir -p /var/snap/multipass/common/cloud-init")
102
+ logger.error(" sudo chown -R $USER:$USER /var/snap/multipass/common/cloud-init")
103
+ logger.error(" sudo chmod -R 755 /var/snap/multipass/common/cloud-init\n")
104
+ # Fall back to user's home directory
105
+ path = Path.home() / ".local" / "share" / "golem" / "provider" / "cloud-init"
106
+
89
107
  elif system == "linux":
90
108
  # Linux without snap
91
- path = Path("/var/lib/multipass/cloud-init")
109
+ path = Path.home() / ".local" / "share" / "golem" / "provider" / "cloud-init"
92
110
  elif system == "darwin":
93
111
  # macOS
94
- path = Path("/Library/Application Support/multipass/cloud-init")
112
+ path = Path.home() / "Library" / "Application Support" / "golem" / "provider" / "cloud-init"
95
113
  elif system == "windows":
96
114
  # Windows
97
- path = Path(os.path.expandvars("%ProgramData%\\Multipass\\cloud-init"))
115
+ path = Path(os.path.expandvars("%LOCALAPPDATA%")) / "golem" / "provider" / "cloud-init"
98
116
  else:
99
117
  path = Path.home() / ".golem" / "provider" / "cloud-init"
100
118
 
provider/main.py CHANGED
@@ -31,11 +31,15 @@ async def setup_provider() -> None:
31
31
  provider = MultipassProvider(resource_tracker, port_manager=port_manager)
32
32
  try:
33
33
  await asyncio.wait_for(provider.initialize(), timeout=30)
34
- app.state.provider = provider
35
34
 
36
- # Store proxy manager reference for cleanup
35
+ # Store provider and proxy manager references
36
+ app.state.provider = provider
37
37
  app.state.proxy_manager = provider.proxy_manager
38
38
 
39
+ # Restore proxy configurations
40
+ logger.process("🔄 Restoring proxy configurations...")
41
+ await app.state.proxy_manager._load_state()
42
+
39
43
  except asyncio.TimeoutError:
40
44
  logger.error("Provider initialization timed out")
41
45
  raise
@@ -0,0 +1,100 @@
1
+ import os
2
+ import subprocess
3
+ import platform
4
+ from pathlib import Path
5
+ from typing import Tuple
6
+
7
+ from .logging import setup_logger
8
+
9
+ logger = setup_logger(__name__)
10
+
11
+ def run_sudo_command(cmd: str) -> Tuple[bool, str]:
12
+ """Run a command with sudo.
13
+
14
+ Args:
15
+ cmd: Command to run
16
+
17
+ Returns:
18
+ Tuple of (success, error_message)
19
+ """
20
+ try:
21
+ # Try non-interactive sudo first
22
+ result = subprocess.run(
23
+ f"sudo -n {cmd}",
24
+ shell=True,
25
+ capture_output=True,
26
+ text=True
27
+ )
28
+ if result.returncode == 0:
29
+ return True, ""
30
+
31
+ # If that fails, try interactive sudo
32
+ logger.warning("Non-interactive sudo failed, will prompt for password")
33
+ result = subprocess.run(
34
+ f"sudo {cmd}",
35
+ shell=True,
36
+ capture_output=True,
37
+ text=True
38
+ )
39
+ if result.returncode == 0:
40
+ return True, ""
41
+
42
+ return False, result.stderr
43
+
44
+ except Exception as e:
45
+ return False, str(e)
46
+
47
+ def setup_cloud_init_dir(path: Path) -> Tuple[bool, str]:
48
+ """Set up cloud-init directory with correct permissions.
49
+
50
+ Args:
51
+ path: Path to cloud-init directory
52
+
53
+ Returns:
54
+ Tuple of (success, error_message)
55
+ """
56
+ if platform.system().lower() != "linux" or not Path("/snap/bin/multipass").exists():
57
+ # Only needed for Linux with snap
58
+ return True, ""
59
+
60
+ try:
61
+ # Create directory
62
+ success, error = run_sudo_command(f"mkdir -p {path}")
63
+ if not success:
64
+ return False, f"Failed to create directory: {error}"
65
+
66
+ # Set ownership
67
+ user = os.environ.get("USER", os.environ.get("USERNAME"))
68
+ success, error = run_sudo_command(f"chown -R {user}:{user} {path}")
69
+ if not success:
70
+ return False, f"Failed to set ownership: {error}"
71
+
72
+ # Set permissions
73
+ success, error = run_sudo_command(f"chmod -R 755 {path}")
74
+ if not success:
75
+ return False, f"Failed to set permissions: {error}"
76
+
77
+ return True, ""
78
+
79
+ except Exception as e:
80
+ return False, str(e)
81
+
82
+ def check_setup_needed() -> bool:
83
+ """Check if setup is needed.
84
+
85
+ Returns:
86
+ True if setup is needed, False otherwise
87
+ """
88
+ # Only needed for Linux with snap
89
+ if platform.system().lower() != "linux" or not Path("/snap/bin/multipass").exists():
90
+ return False
91
+
92
+ # Check if setup has already been completed
93
+ setup_flag = Path.home() / ".golem" / "provider" / ".setup-complete"
94
+ return not setup_flag.exists()
95
+
96
+ def mark_setup_complete() -> None:
97
+ """Mark setup as complete."""
98
+ setup_flag = Path.home() / ".golem" / "provider" / ".setup-complete"
99
+ setup_flag.parent.mkdir(parents=True, exist_ok=True)
100
+ setup_flag.touch()
@@ -158,18 +158,31 @@ class PythonProxyManager:
158
158
  self.port_manager = port_manager
159
159
  self.state_file = state_file or os.path.expanduser("~/.golem/provider/proxy_state.json")
160
160
  self._proxies: Dict[str, ProxyServer] = {} # vm_id -> ProxyServer
161
- self._load_state()
161
+ # Note: _load_state is now async and will be called explicitly during provider setup
162
162
 
163
- def _load_state(self) -> None:
164
- """Load proxy state from file."""
163
+ async def _load_state(self) -> None:
164
+ """Load and restore proxy state from file."""
165
165
  try:
166
166
  state_path = Path(self.state_file)
167
167
  if state_path.exists():
168
168
  with open(state_path, 'r') as f:
169
169
  state = json.load(f)
170
- # We only need to restore port allocations
171
- # Actual proxy servers will be recreated as needed
172
- logger.info(f"Loaded proxy state for {len(state)} VMs")
170
+ # Restore proxy servers from saved state
171
+ restore_tasks = []
172
+ for vm_id, proxy_info in state.items():
173
+ # Create task to restore proxy
174
+ task = self.add_vm(
175
+ vm_id=vm_id,
176
+ vm_ip=proxy_info['target'],
177
+ port=proxy_info['port']
178
+ )
179
+ restore_tasks.append(task)
180
+
181
+ # Wait for all proxies to be restored
182
+ if restore_tasks:
183
+ results = await asyncio.gather(*restore_tasks, return_exceptions=True)
184
+ successful = sum(1 for r in results if r is True)
185
+ logger.info(f"Restored {successful}/{len(state)} proxy configurations")
173
186
  except Exception as e:
174
187
  logger.error(f"Failed to load proxy state: {e}")
175
188