golem-vm-provider 0.1.16__py3-none-any.whl → 0.1.17__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.16
3
+ Version: 0.1.17
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,7 +2,7 @@ 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=tF51xZqu-7x_cmHS3lQaAZorjk7DTZOZuOlU-O8Gk9k,11204
5
+ provider/config.py,sha256=btSyU6cB6z8o9nHWpNEOBDxKqsEha1yStM-6of3Np0s,13237
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
@@ -14,13 +14,13 @@ provider/utils/logging.py,sha256=C_elr0sJROHKQgErYpHJQvfujgh0k4Zf2gg8ZKfrmVk,259
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
16
  provider/vm/__init__.py,sha256=JGs50tUmzOR1rQ_w4fMY_3XWylmiA1G7KKWZkVw51mY,501
17
- provider/vm/cloud_init.py,sha256=lgOlpiWTtdZi6jbYceA4XERITAfJZjWi12XI3aAIIF8,3267
17
+ provider/vm/cloud_init.py,sha256=E5dDH7dqStRcJNDfbarBBe83-c9N63W8B5ycIrHI8eU,4627
18
18
  provider/vm/models.py,sha256=zkfvP5Z50SPDNajwZTt9NTDIMRQIsZLvSOsuirHEcJM,6256
19
19
  provider/vm/multipass.py,sha256=FOcsfcJ-NrgBg_fvq_CKOKsQ0xOmk7Z34KXi3ag_Vl8,16603
20
20
  provider/vm/name_mapper.py,sha256=MrshNeJ4Dw-WBsyiIVcn9N5xyOxaBKX4Yqhyh_m5IFg,4103
21
21
  provider/vm/port_manager.py,sha256=d03uwU76vx6LgADMN8ffBT9t400XQ3vtYlXr6cLIFN0,9831
22
22
  provider/vm/proxy_manager.py,sha256=cu0FPPbeCc3CR6NRE_CnLjiRg7xVdSFUylVUOL1g1sI,10154
23
- golem_vm_provider-0.1.16.dist-info/METADATA,sha256=8pEYP425VQlfm1bpPWB5RiQ-1eaftoMAvox-T7VeW_w,10594
24
- golem_vm_provider-0.1.16.dist-info/WHEEL,sha256=XbeZDeTWKc1w7CSIyre5aMDU_-PohRwTQceYnisIYYY,88
25
- golem_vm_provider-0.1.16.dist-info/entry_points.txt,sha256=E4rCWo_Do_2zCG_GewNuftfVlHF_8b_OvioZre0dfeA,54
26
- golem_vm_provider-0.1.16.dist-info/RECORD,,
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,,
provider/config.py CHANGED
@@ -57,26 +57,72 @@ class Settings(BaseSettings):
57
57
  VM_DATA_DIR: str = ""
58
58
  SSH_KEY_DIR: str = ""
59
59
  CLOUD_INIT_DIR: str = ""
60
+ CLOUD_INIT_FALLBACK_DIR: str = "" # Will be set to a temp directory if needed
60
61
 
61
62
  @validator("CLOUD_INIT_DIR", pre=True)
62
63
  def resolve_cloud_init_dir(cls, v: str) -> str:
63
64
  """Resolve and create cloud-init directory path."""
64
- if not v:
65
- path = Path.home() / ".golem" / "provider" / "cloud-init"
66
- else:
65
+ import platform
66
+ import tempfile
67
+
68
+ def verify_dir_permissions(path: Path) -> bool:
69
+ """Verify directory has correct permissions and is accessible."""
70
+ try:
71
+ # Create test file
72
+ test_file = path / "permission_test"
73
+ test_file.write_text("test")
74
+ test_file.unlink()
75
+ return True
76
+ except Exception:
77
+ return False
78
+
79
+ if v:
67
80
  path = Path(v)
68
81
  if not path.is_absolute():
69
82
  path = Path.home() / path
70
-
83
+ else:
84
+ system = platform.system().lower()
85
+ # Try OS-specific paths first
86
+ if system == "linux" and Path("/snap/bin/multipass").exists():
87
+ # Linux with snap
88
+ path = Path("/var/snap/multipass/common/cloud-init")
89
+ elif system == "linux":
90
+ # Linux without snap
91
+ path = Path("/var/lib/multipass/cloud-init")
92
+ elif system == "darwin":
93
+ # macOS
94
+ path = Path("/Library/Application Support/multipass/cloud-init")
95
+ elif system == "windows":
96
+ # Windows
97
+ path = Path(os.path.expandvars("%ProgramData%\\Multipass\\cloud-init"))
98
+ else:
99
+ path = Path.home() / ".golem" / "provider" / "cloud-init"
100
+
71
101
  try:
102
+ # Try to create and verify the directory
72
103
  path.mkdir(parents=True, exist_ok=True)
73
- path.chmod(0o755) # Readable and executable by owner and others, writable by owner
74
- logger.debug(f"Created cloud-init directory at {path}")
104
+ if platform.system().lower() != "windows":
105
+ path.chmod(0o755) # Readable and executable by owner and others, writable by owner
106
+
107
+ if verify_dir_permissions(path):
108
+ logger.debug(f"Created cloud-init directory at {path}")
109
+ return str(path)
110
+
111
+ # If verification fails, fall back to temp directory
112
+ fallback_path = Path(tempfile.gettempdir()) / "golem" / "cloud-init"
113
+ fallback_path.mkdir(parents=True, exist_ok=True)
114
+ if platform.system().lower() != "windows":
115
+ fallback_path.chmod(0o755)
116
+
117
+ if verify_dir_permissions(fallback_path):
118
+ logger.warning(f"Using fallback cloud-init directory at {fallback_path}")
119
+ return str(fallback_path)
120
+
121
+ raise ValueError("Could not create a writable cloud-init directory")
122
+
75
123
  except Exception as e:
76
124
  logger.error(f"Failed to create cloud-init directory at {path}: {e}")
77
125
  raise ValueError(f"Failed to create cloud-init directory: {e}")
78
-
79
- return str(path)
80
126
 
81
127
  @validator("VM_DATA_DIR", pre=True)
82
128
  def resolve_vm_data_dir(cls, v: str) -> str:
provider/vm/cloud_init.py CHANGED
@@ -1,5 +1,6 @@
1
1
  import yaml
2
2
  import os
3
+ import subprocess
3
4
  from datetime import datetime
4
5
  from pathlib import Path
5
6
  from typing import Dict, Optional, Tuple
@@ -9,6 +10,29 @@ from ..utils.logging import setup_logger
9
10
 
10
11
  logger = setup_logger(__name__)
11
12
 
13
+ def validate_cloud_init(content: str) -> bool:
14
+ """Validate cloud-init configuration content.
15
+
16
+ Args:
17
+ content: YAML content to validate
18
+
19
+ Returns:
20
+ True if valid, False otherwise
21
+ """
22
+ try:
23
+ # First validate YAML syntax
24
+ yaml.safe_load(content)
25
+
26
+ # Check for required #cloud-config header
27
+ if not content.startswith("#cloud-config\n"):
28
+ logger.error("Cloud-init config missing #cloud-config header")
29
+ return False
30
+
31
+ return True
32
+ except Exception as e:
33
+ logger.error(f"Cloud-init validation failed: {e}")
34
+ return False
35
+
12
36
  def generate_cloud_init(
13
37
  hostname: str,
14
38
  ssh_key: str,
@@ -32,10 +56,15 @@ def generate_cloud_init(
32
56
 
33
57
  logger.info(f"Generating cloud-init configuration {config_id}")
34
58
  try:
59
+ # Start with required #cloud-config header
60
+ yaml_content = "#cloud-config\n"
61
+
35
62
  config = {
63
+ "version": 1,
36
64
  "hostname": hostname,
37
65
  "package_update": True,
38
66
  "package_upgrade": True,
67
+ "preserve_hostname": False,
39
68
  "ssh_authorized_keys": [ssh_key],
40
69
  "users": [{
41
70
  "name": "root",
@@ -60,16 +89,21 @@ def generate_cloud_init(
60
89
  if runcmd:
61
90
  config["runcmd"].extend(runcmd)
62
91
 
63
- # Validate YAML before writing
64
- yaml_content = yaml.safe_dump(config)
65
- yaml.safe_load(yaml_content) # Validate by parsing
92
+ # Add config to YAML content with document markers
93
+ yaml_content += "---\n"
94
+ yaml_content += yaml.safe_dump(config, default_flow_style=False, sort_keys=False)
95
+
96
+ # Validate the configuration
97
+ if not validate_cloud_init(yaml_content):
98
+ raise Exception("Cloud-init configuration validation failed")
66
99
 
67
100
  # Write to file in our managed directory
68
101
  with open(config_path, 'w') as f:
69
102
  f.write(yaml_content)
70
103
 
71
104
  # Set proper permissions
72
- config_path.chmod(0o644) # World readable but only owner writable
105
+ if os.name != 'nt': # Skip on Windows
106
+ config_path.chmod(0o644) # World readable but only owner writable
73
107
 
74
108
  logger.debug(f"Cloud-init configuration written to {config_path}")
75
109
  logger.debug(f"Cloud-init configuration content:\n{yaml_content}")
@@ -82,6 +116,11 @@ def generate_cloud_init(
82
116
  # Don't cleanup on error - keep file for debugging
83
117
  if config_path.exists():
84
118
  logger.info(f"Failed config preserved at {config_path} for debugging")
119
+ # Log the file contents for debugging
120
+ try:
121
+ logger.debug(f"Failed config contents:\n{config_path.read_text()}")
122
+ except Exception as read_error:
123
+ logger.error(f"Could not read failed config: {read_error}")
85
124
  raise Exception(error_msg)
86
125
 
87
126
  def cleanup_cloud_init(path: str, config_id: str) -> None: