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.
- {golem_vm_provider-0.1.14 → golem_vm_provider-0.1.16}/PKG-INFO +1 -1
- {golem_vm_provider-0.1.14 → golem_vm_provider-0.1.16}/provider/config.py +21 -0
- golem_vm_provider-0.1.16/provider/vm/cloud_init.py +98 -0
- {golem_vm_provider-0.1.14 → golem_vm_provider-0.1.16}/provider/vm/multipass.py +11 -8
- {golem_vm_provider-0.1.14 → golem_vm_provider-0.1.16}/pyproject.toml +1 -1
- golem_vm_provider-0.1.14/provider/vm/cloud_init.py +0 -67
- {golem_vm_provider-0.1.14 → golem_vm_provider-0.1.16}/README.md +0 -0
- {golem_vm_provider-0.1.14 → golem_vm_provider-0.1.16}/provider/__init__.py +0 -0
- {golem_vm_provider-0.1.14 → golem_vm_provider-0.1.16}/provider/api/__init__.py +0 -0
- {golem_vm_provider-0.1.14 → golem_vm_provider-0.1.16}/provider/api/models.py +0 -0
- {golem_vm_provider-0.1.14 → golem_vm_provider-0.1.16}/provider/api/routes.py +0 -0
- {golem_vm_provider-0.1.14 → golem_vm_provider-0.1.16}/provider/discovery/__init__.py +0 -0
- {golem_vm_provider-0.1.14 → golem_vm_provider-0.1.16}/provider/discovery/advertiser.py +0 -0
- {golem_vm_provider-0.1.14 → golem_vm_provider-0.1.16}/provider/discovery/resource_tracker.py +0 -0
- {golem_vm_provider-0.1.14 → golem_vm_provider-0.1.16}/provider/main.py +0 -0
- {golem_vm_provider-0.1.14 → golem_vm_provider-0.1.16}/provider/network/port_verifier.py +0 -0
- {golem_vm_provider-0.1.14 → golem_vm_provider-0.1.16}/provider/security/ethereum.py +0 -0
- {golem_vm_provider-0.1.14 → golem_vm_provider-0.1.16}/provider/utils/ascii_art.py +0 -0
- {golem_vm_provider-0.1.14 → golem_vm_provider-0.1.16}/provider/utils/logging.py +0 -0
- {golem_vm_provider-0.1.14 → golem_vm_provider-0.1.16}/provider/utils/port_display.py +0 -0
- {golem_vm_provider-0.1.14 → golem_vm_provider-0.1.16}/provider/utils/retry.py +0 -0
- {golem_vm_provider-0.1.14 → golem_vm_provider-0.1.16}/provider/vm/__init__.py +0 -0
- {golem_vm_provider-0.1.14 → golem_vm_provider-0.1.16}/provider/vm/models.py +0 -0
- {golem_vm_provider-0.1.14 → golem_vm_provider-0.1.16}/provider/vm/name_mapper.py +0 -0
- {golem_vm_provider-0.1.14 → golem_vm_provider-0.1.16}/provider/vm/port_manager.py +0 -0
- {golem_vm_provider-0.1.14 → golem_vm_provider-0.1.16}/provider/vm/proxy_manager.py +0 -0
@@ -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
|
-
|
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.
|
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
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{golem_vm_provider-0.1.14 → golem_vm_provider-0.1.16}/provider/discovery/resource_tracker.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|