golem-vm-provider 0.1.14__tar.gz → 0.1.16__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 (26) hide show
  1. {golem_vm_provider-0.1.14 → golem_vm_provider-0.1.16}/PKG-INFO +1 -1
  2. {golem_vm_provider-0.1.14 → golem_vm_provider-0.1.16}/provider/config.py +21 -0
  3. golem_vm_provider-0.1.16/provider/vm/cloud_init.py +98 -0
  4. {golem_vm_provider-0.1.14 → golem_vm_provider-0.1.16}/provider/vm/multipass.py +11 -8
  5. {golem_vm_provider-0.1.14 → golem_vm_provider-0.1.16}/pyproject.toml +1 -1
  6. golem_vm_provider-0.1.14/provider/vm/cloud_init.py +0 -67
  7. {golem_vm_provider-0.1.14 → golem_vm_provider-0.1.16}/README.md +0 -0
  8. {golem_vm_provider-0.1.14 → golem_vm_provider-0.1.16}/provider/__init__.py +0 -0
  9. {golem_vm_provider-0.1.14 → golem_vm_provider-0.1.16}/provider/api/__init__.py +0 -0
  10. {golem_vm_provider-0.1.14 → golem_vm_provider-0.1.16}/provider/api/models.py +0 -0
  11. {golem_vm_provider-0.1.14 → golem_vm_provider-0.1.16}/provider/api/routes.py +0 -0
  12. {golem_vm_provider-0.1.14 → golem_vm_provider-0.1.16}/provider/discovery/__init__.py +0 -0
  13. {golem_vm_provider-0.1.14 → golem_vm_provider-0.1.16}/provider/discovery/advertiser.py +0 -0
  14. {golem_vm_provider-0.1.14 → golem_vm_provider-0.1.16}/provider/discovery/resource_tracker.py +0 -0
  15. {golem_vm_provider-0.1.14 → golem_vm_provider-0.1.16}/provider/main.py +0 -0
  16. {golem_vm_provider-0.1.14 → golem_vm_provider-0.1.16}/provider/network/port_verifier.py +0 -0
  17. {golem_vm_provider-0.1.14 → golem_vm_provider-0.1.16}/provider/security/ethereum.py +0 -0
  18. {golem_vm_provider-0.1.14 → golem_vm_provider-0.1.16}/provider/utils/ascii_art.py +0 -0
  19. {golem_vm_provider-0.1.14 → golem_vm_provider-0.1.16}/provider/utils/logging.py +0 -0
  20. {golem_vm_provider-0.1.14 → golem_vm_provider-0.1.16}/provider/utils/port_display.py +0 -0
  21. {golem_vm_provider-0.1.14 → golem_vm_provider-0.1.16}/provider/utils/retry.py +0 -0
  22. {golem_vm_provider-0.1.14 → golem_vm_provider-0.1.16}/provider/vm/__init__.py +0 -0
  23. {golem_vm_provider-0.1.14 → golem_vm_provider-0.1.16}/provider/vm/models.py +0 -0
  24. {golem_vm_provider-0.1.14 → golem_vm_provider-0.1.16}/provider/vm/name_mapper.py +0 -0
  25. {golem_vm_provider-0.1.14 → golem_vm_provider-0.1.16}/provider/vm/port_manager.py +0 -0
  26. {golem_vm_provider-0.1.14 → golem_vm_provider-0.1.16}/provider/vm/proxy_manager.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: golem-vm-provider
3
- Version: 0.1.14
3
+ Version: 0.1.16
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
@@ -56,6 +56,27 @@ class Settings(BaseSettings):
56
56
  DEFAULT_VM_IMAGE: str = "ubuntu:24.04"
57
57
  VM_DATA_DIR: str = ""
58
58
  SSH_KEY_DIR: str = ""
59
+ CLOUD_INIT_DIR: str = ""
60
+
61
+ @validator("CLOUD_INIT_DIR", pre=True)
62
+ def resolve_cloud_init_dir(cls, v: str) -> str:
63
+ """Resolve and create cloud-init directory path."""
64
+ if not v:
65
+ path = Path.home() / ".golem" / "provider" / "cloud-init"
66
+ else:
67
+ path = Path(v)
68
+ if not path.is_absolute():
69
+ path = Path.home() / path
70
+
71
+ try:
72
+ 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}")
75
+ except Exception as e:
76
+ logger.error(f"Failed to create cloud-init directory at {path}: {e}")
77
+ raise ValueError(f"Failed to create cloud-init directory: {e}")
78
+
79
+ return str(path)
59
80
 
60
81
  @validator("VM_DATA_DIR", pre=True)
61
82
  def resolve_vm_data_dir(cls, v: str) -> str:
@@ -0,0 +1,98 @@
1
+ import yaml
2
+ import os
3
+ from datetime import datetime
4
+ from pathlib import Path
5
+ from typing import Dict, Optional, Tuple
6
+
7
+ from ..config import settings
8
+ from ..utils.logging import setup_logger
9
+
10
+ logger = setup_logger(__name__)
11
+
12
+ def generate_cloud_init(
13
+ hostname: str,
14
+ ssh_key: str,
15
+ packages: Optional[list[str]] = None,
16
+ runcmd: Optional[list[str]] = None
17
+ ) -> Tuple[str, str]:
18
+ """Generate cloud-init configuration.
19
+
20
+ Args:
21
+ hostname: VM hostname
22
+ ssh_key: SSH public key to add to authorized_keys
23
+ packages: List of packages to install
24
+ runcmd: List of commands to run on first boot
25
+
26
+ Returns:
27
+ Tuple of (path to cloud-init configuration file, config_id for debugging)
28
+ """
29
+ # Generate unique config ID for this cloud-init file
30
+ config_id = f"{hostname}-{datetime.now().strftime('%Y%m%d-%H%M%S')}"
31
+ config_path = Path(settings.CLOUD_INIT_DIR) / f"{config_id}.yaml"
32
+
33
+ logger.info(f"Generating cloud-init configuration {config_id}")
34
+ try:
35
+ config = {
36
+ "hostname": hostname,
37
+ "package_update": True,
38
+ "package_upgrade": True,
39
+ "ssh_authorized_keys": [ssh_key],
40
+ "users": [{
41
+ "name": "root",
42
+ "ssh_authorized_keys": [ssh_key]
43
+ }],
44
+ "write_files": [
45
+ {
46
+ "path": "/etc/ssh/sshd_config.d/allow_root.conf",
47
+ "content": "PermitRootLogin prohibit-password\n",
48
+ "owner": "root:root",
49
+ "permissions": "0644"
50
+ }
51
+ ],
52
+ "runcmd": [
53
+ "systemctl restart ssh"
54
+ ]
55
+ }
56
+
57
+ if packages:
58
+ config["packages"] = packages
59
+
60
+ if runcmd:
61
+ config["runcmd"].extend(runcmd)
62
+
63
+ # Validate YAML before writing
64
+ yaml_content = yaml.safe_dump(config)
65
+ yaml.safe_load(yaml_content) # Validate by parsing
66
+
67
+ # Write to file in our managed directory
68
+ with open(config_path, 'w') as f:
69
+ f.write(yaml_content)
70
+
71
+ # Set proper permissions
72
+ config_path.chmod(0o644) # World readable but only owner writable
73
+
74
+ logger.debug(f"Cloud-init configuration written to {config_path}")
75
+ logger.debug(f"Cloud-init configuration content:\n{yaml_content}")
76
+
77
+ return str(config_path), config_id
78
+
79
+ except Exception as e:
80
+ error_msg = f"Failed to generate cloud-init configuration: {str(e)}"
81
+ logger.error(f"{error_msg}\nConfig ID: {config_id}")
82
+ # Don't cleanup on error - keep file for debugging
83
+ if config_path.exists():
84
+ logger.info(f"Failed config preserved at {config_path} for debugging")
85
+ raise Exception(error_msg)
86
+
87
+ def cleanup_cloud_init(path: str, config_id: str) -> None:
88
+ """Clean up cloud-init configuration file.
89
+
90
+ Args:
91
+ path: Path to cloud-init configuration file
92
+ config_id: Configuration ID for logging
93
+ """
94
+ try:
95
+ Path(path).unlink()
96
+ logger.debug(f"Cleaned up cloud-init configuration {config_id}")
97
+ except Exception as e:
98
+ logger.warning(f"Failed to cleanup cloud-init configuration {config_id}: {e}")
@@ -158,20 +158,22 @@ class MultipassProvider(VMProvider):
158
158
  """
159
159
  multipass_name = f"golem-{config.name}-{datetime.now().strftime('%Y%m%d-%H%M%S')}"
160
160
  await self.name_mapper.add_mapping(config.name, multipass_name)
161
+ cloud_init_path = None
162
+ config_id = None
161
163
 
162
164
  # Verify resources are properly allocated
163
165
  if not self.resource_tracker.can_accept_resources(config.resources):
164
166
  raise VMCreateError("Resources not properly allocated or insufficient")
165
167
 
166
- # Generate cloud-init config with requestor's public key
167
- cloud_init_path = generate_cloud_init(
168
- hostname=config.name,
169
- ssh_key=config.ssh_key
170
- )
171
-
172
168
  try:
169
+ # Generate cloud-init config with requestor's public key
170
+ cloud_init_path, config_id = generate_cloud_init(
171
+ hostname=config.name,
172
+ ssh_key=config.ssh_key
173
+ )
174
+
173
175
  # Launch VM
174
- logger.process(f"🚀 Launching VM {multipass_name}")
176
+ logger.process(f"🚀 Launching VM {multipass_name} with config {config_id}")
175
177
  launch_cmd = [
176
178
  "launch",
177
179
  config.image,
@@ -237,7 +239,8 @@ class MultipassProvider(VMProvider):
237
239
 
238
240
  finally:
239
241
  # Cleanup cloud-init file
240
- cleanup_cloud_init(cloud_init_path)
242
+ if cloud_init_path and config_id:
243
+ cleanup_cloud_init(cloud_init_path, config_id)
241
244
 
242
245
  def _verify_vm_exists(self, vm_id: str) -> bool:
243
246
  """Check if VM exists in multipass.
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "golem-vm-provider"
3
- version = "0.1.14"
3
+ version = "0.1.16"
4
4
  description = "VM on Golem Provider Node - Run your own provider node to offer VMs on the Golem Network"
5
5
  authors = ["Phillip Jensen <phillip+vm-on-golem@golemgrid.com>"]
6
6
  readme = "README.md"
@@ -1,67 +0,0 @@
1
- import yaml
2
- import tempfile
3
- from pathlib import Path
4
- from typing import Dict, Optional
5
-
6
- def generate_cloud_init(
7
- hostname: str,
8
- ssh_key: str,
9
- packages: Optional[list[str]] = None,
10
- runcmd: Optional[list[str]] = None
11
- ) -> str:
12
- """Generate cloud-init configuration.
13
-
14
- Args:
15
- hostname: VM hostname
16
- ssh_key: SSH public key to add to authorized_keys
17
- packages: List of packages to install
18
- runcmd: List of commands to run on first boot
19
-
20
- Returns:
21
- Path to cloud-init configuration file
22
- """
23
- config = {
24
- "hostname": hostname,
25
- "package_update": True,
26
- "package_upgrade": True,
27
- "ssh_authorized_keys": [ssh_key],
28
- "users": [{
29
- "name": "root",
30
- "ssh_authorized_keys": [ssh_key]
31
- }],
32
- "write_files": [
33
- {
34
- "path": "/etc/ssh/sshd_config.d/allow_root.conf",
35
- "content": "PermitRootLogin prohibit-password\n",
36
- "owner": "root:root",
37
- "permissions": "0644"
38
- }
39
- ],
40
- "runcmd": [
41
- "systemctl restart ssh"
42
- ]
43
- }
44
-
45
- if packages:
46
- config["packages"] = packages
47
-
48
- if runcmd:
49
- config["runcmd"].extend(runcmd)
50
-
51
- # Create temporary file
52
- temp_file = tempfile.NamedTemporaryFile(mode='w', suffix='.yaml', delete=False)
53
- yaml.safe_dump(config, temp_file)
54
- temp_file.close()
55
-
56
- return temp_file.name
57
-
58
- def cleanup_cloud_init(path: str) -> None:
59
- """Clean up cloud-init configuration file.
60
-
61
- Args:
62
- path: Path to cloud-init configuration file
63
- """
64
- try:
65
- Path(path).unlink()
66
- except Exception:
67
- pass