fix(vps): report stable creation progress
Build and publish Docker image / build-and-publish (push) Successful in 9s
Build and publish Docker image / build-and-publish (push) Successful in 9s
This commit is contained in:
1 parent
9822e43592
commit
522e5c5634
2 files changed
+193
-90
No files matched your search
+100
-54
@@ -786,13 +786,16 @@ def create_vps_server(config: Dict, user_id: str, server_id: str, namespace: str
|
||||
memory_request = f"{ram_gb * 0.5}Gi"
|
||||
|
||||
if config['image'] == 'windows':
|
||||
return create_windows_vps(config, user_id, server_id, namespace, image_config, ports, storage_size, cpu_cores, memory_limit, memory_request)
|
||||
return create_windows_vps(
|
||||
config, user_id, server_id, namespace, image_config, ports,
|
||||
storage_size, cpu_cores, memory_limit, memory_request, progress_callback
|
||||
)
|
||||
|
||||
# Create DataVolume
|
||||
data_volume_name = f"{server_id}-rootdisk"
|
||||
|
||||
if progress_callback:
|
||||
progress_callback('Initializing', 0, 'Preparing VPS creation...')
|
||||
progress_callback('Preparing storage', 3, 'Preparing the VPS system disk.')
|
||||
|
||||
data_volume_body = {
|
||||
'apiVersion': 'cdi.kubevirt.io/v1beta1',
|
||||
@@ -978,11 +981,13 @@ final_message: "AnchorHost VPS ready! User: {image_config['user']}, Password: {i
|
||||
if dv_created and k8s_manager.connected:
|
||||
logger.info("⏳ Waiting for DataVolume to be ready...")
|
||||
if progress_callback:
|
||||
progress_callback('Pending', 0, 'Creating DataVolume...')
|
||||
progress_callback('Allocating storage', 5, 'Allocating storage for the system image.')
|
||||
|
||||
max_wait = 600
|
||||
wait_interval = 5 # Check more frequently for better progress updates
|
||||
wait_interval = 5
|
||||
elapsed = 0
|
||||
latest_progress = 12
|
||||
image_ready = False
|
||||
|
||||
while elapsed < max_wait:
|
||||
try:
|
||||
@@ -998,67 +1003,75 @@ final_message: "AnchorHost VPS ready! User: {image_config['user']}, Password: {i
|
||||
phase = status.get('phase', '')
|
||||
progress = status.get('progress', '')
|
||||
|
||||
# Extract progress percentage if available
|
||||
progress_percent = 0
|
||||
# CDI reports the actual image download percentage.
|
||||
progress_percent = None
|
||||
if progress:
|
||||
# Progress format is usually "XX.X%" or "XX.XX%"
|
||||
try:
|
||||
progress_percent = float(progress.replace('%', ''))
|
||||
except (ValueError, AttributeError):
|
||||
pass
|
||||
|
||||
# Map phase to progress percentage if not available
|
||||
if progress_percent == 0:
|
||||
phase_progress_map = {
|
||||
'Pending': 5,
|
||||
'PVCBound': 10,
|
||||
'ImportScheduled': 15,
|
||||
'ImportInProgress': 50,
|
||||
'Succeeded': 100
|
||||
}
|
||||
progress_percent = phase_progress_map.get(phase, 0)
|
||||
|
||||
# Create progress message
|
||||
phase_messages = {
|
||||
'Pending': 'Waiting for storage to be allocated...',
|
||||
'PVCBound': 'Storage allocated, preparing import...',
|
||||
'ImportScheduled': 'Import scheduled, starting download...',
|
||||
'ImportInProgress': f'Downloading image... ({progress if progress else "in progress"})',
|
||||
'Succeeded': 'Image download completed!',
|
||||
'Failed': 'Image download failed'
|
||||
phase_progress_map = {
|
||||
'Pending': 5,
|
||||
'PVCBound': 9,
|
||||
'ImportScheduled': 12,
|
||||
'Succeeded': 84
|
||||
}
|
||||
message = phase_messages.get(phase, f'Processing... ({phase})')
|
||||
if phase == 'ImportInProgress' and progress_percent is not None:
|
||||
progress_percent = 12 + max(0, min(100, progress_percent)) * 0.72
|
||||
else:
|
||||
progress_percent = phase_progress_map.get(phase, latest_progress)
|
||||
latest_progress = max(latest_progress, int(progress_percent))
|
||||
|
||||
phase_messages = {
|
||||
'Pending': ('Allocating storage', 'Waiting for the storage volume.'),
|
||||
'PVCBound': ('Preparing image download', 'Storage is ready. Preparing the system image.'),
|
||||
'ImportScheduled': ('Starting image download', 'The system image download has been scheduled.'),
|
||||
'ImportInProgress': ('Downloading system image', f'Downloading the system image ({progress or "in progress"}).'),
|
||||
'Succeeded': ('System image ready', 'The system image is ready.')
|
||||
}
|
||||
progress_phase, message = phase_messages.get(
|
||||
phase, ('Preparing system image', 'Waiting for the system image importer.')
|
||||
)
|
||||
|
||||
# Send progress update
|
||||
if progress_callback:
|
||||
progress_callback(phase, progress_percent, message)
|
||||
progress_callback(progress_phase, latest_progress, message)
|
||||
|
||||
if phase == 'Succeeded':
|
||||
logger.info("✅ DataVolume ready!")
|
||||
logger.info("VPS system image is ready")
|
||||
image_ready = True
|
||||
break
|
||||
elif phase == 'Failed':
|
||||
error_message = status.get('conditions', [{}])[0].get('message', 'Unknown error') if status.get('conditions') else 'Unknown error'
|
||||
logger.error(f"❌ DataVolume failed: {error_message}")
|
||||
logger.error(f"VPS system image import failed: {error_message}")
|
||||
if progress_callback:
|
||||
progress_callback('Failed', 0, f'Image download failed: {error_message}')
|
||||
break
|
||||
progress_callback('Failed', latest_progress, f'System image download failed: {error_message}')
|
||||
raise RuntimeError(f'System image download failed: {error_message}')
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"⚠ Error checking DataVolume: {e}")
|
||||
if isinstance(e, RuntimeError):
|
||||
raise
|
||||
logger.warning(f"Could not read VPS image import status: {e}")
|
||||
if progress_callback:
|
||||
progress_callback('Error', 0, f'Error checking progress: {str(e)}')
|
||||
progress_callback(
|
||||
'Downloading system image', latest_progress,
|
||||
'Waiting for the image download status to update.'
|
||||
)
|
||||
|
||||
if image_ready:
|
||||
break
|
||||
time.sleep(wait_interval)
|
||||
elapsed += wait_interval
|
||||
|
||||
if elapsed >= max_wait:
|
||||
logger.warning(f"⚠ DataVolume wait timeout after {max_wait}s")
|
||||
logger.warning(f"VPS image import did not finish within {max_wait}s")
|
||||
if progress_callback:
|
||||
progress_callback('Timeout', 0, 'Image download timed out, proceeding anyway...')
|
||||
progress_callback('Failed', latest_progress, 'The system image download timed out.')
|
||||
raise TimeoutError('System image download timed out; the virtual machine was not started.')
|
||||
|
||||
# Create VM
|
||||
if progress_callback:
|
||||
progress_callback('CreatingVM', 90, 'Creating virtual machine...')
|
||||
progress_callback('Starting virtual machine', 88, 'Creating the virtual machine resource.')
|
||||
|
||||
try:
|
||||
custom_objects_api.create_namespaced_custom_object(
|
||||
@@ -1068,15 +1081,16 @@ final_message: "AnchorHost VPS ready! User: {image_config['user']}, Password: {i
|
||||
plural="virtualmachines",
|
||||
body=vm_body
|
||||
)
|
||||
logger.info(f"✅ Created VirtualMachine {server_id}")
|
||||
if progress_callback:
|
||||
progress_callback('Completed', 100, 'VPS created successfully!')
|
||||
logger.info(f"Created VirtualMachine {server_id}")
|
||||
except Exception as e:
|
||||
logger.error(f"❌ Failed to create VM: {e}")
|
||||
if progress_callback:
|
||||
progress_callback('Error', 0, f'Failed to create VM: {str(e)}')
|
||||
raise Exception(f"Failed to create VM: {str(e)}")
|
||||
|
||||
if progress_callback:
|
||||
progress_callback('Configuring network', 92, 'Setting up the VPS network and ports.')
|
||||
|
||||
# Create Service with FIXED port handling
|
||||
if ports:
|
||||
service_ports = []
|
||||
@@ -1123,17 +1137,14 @@ final_message: "AnchorHost VPS ready! User: {image_config['user']}, Password: {i
|
||||
ports.extend(actual_ports)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"⚠ Failed to create service: {e}")
|
||||
|
||||
# Background status sync
|
||||
if k8s_manager.connected:
|
||||
threading.Thread(
|
||||
target=wait_and_sync_vm_status,
|
||||
args=(server_id, user_id, namespace),
|
||||
daemon=True
|
||||
).start()
|
||||
logger.error(f"Failed to create VPS network service: {e}")
|
||||
if progress_callback:
|
||||
progress_callback('Failed', 92, 'Could not create the VPS network service.')
|
||||
raise RuntimeError(f'Failed to create VPS network service: {e}') from e
|
||||
|
||||
# Create server record
|
||||
if progress_callback:
|
||||
progress_callback('Registering VPS', 97, 'Saving the VPS to your server list.')
|
||||
server_data = {
|
||||
'id': server_id,
|
||||
'name': config['name'],
|
||||
@@ -1151,7 +1162,18 @@ final_message: "AnchorHost VPS ready! User: {image_config['user']}, Password: {i
|
||||
'k8s_name': server_id
|
||||
}
|
||||
|
||||
return create_server_record(server_data)
|
||||
server = create_server_record(server_data)
|
||||
if not server:
|
||||
raise RuntimeError('VPS resources were created, but the server record could not be saved.')
|
||||
|
||||
if k8s_manager.connected:
|
||||
threading.Thread(
|
||||
target=wait_and_sync_vm_status,
|
||||
args=(server_id, user_id, namespace),
|
||||
daemon=True
|
||||
).start()
|
||||
|
||||
return server
|
||||
|
||||
def wait_and_sync_vm_status(server_id: str, user_id: str, namespace: str):
|
||||
"""Background VM status sync"""
|
||||
@@ -1240,9 +1262,12 @@ def wait_and_sync_pod_status(server_id: str, user_id: str, namespace: str):
|
||||
sync_server_status_from_k8s(server_id, user_id)
|
||||
|
||||
def create_windows_vps(config: Dict, user_id: str, server_id: str, namespace: str, image_config: Dict,
|
||||
ports: List, storage_size: str, cpu_cores: int, memory_limit: str, memory_request: str) -> Dict:
|
||||
ports: List, storage_size: str, cpu_cores: int, memory_limit: str, memory_request: str,
|
||||
progress_callback=None) -> Dict:
|
||||
"""Create Windows VPS"""
|
||||
pvc_name = f"{server_id}-rootdisk"
|
||||
if progress_callback:
|
||||
progress_callback('Preparing storage', 5, 'Preparing storage for the Windows VPS.')
|
||||
if not k8s_manager.create_pvc(namespace, pvc_name, storage_size):
|
||||
raise Exception("Failed to create PVC")
|
||||
|
||||
@@ -1281,6 +1306,8 @@ def create_windows_vps(config: Dict, user_id: str, server_id: str, namespace: st
|
||||
}
|
||||
}
|
||||
|
||||
if progress_callback:
|
||||
progress_callback('Starting virtual machine', 88, 'Creating the Windows virtual machine resource.')
|
||||
custom_objects_api.create_namespaced_custom_object(
|
||||
group="kubevirt.io",
|
||||
version="v1",
|
||||
@@ -1289,6 +1316,9 @@ def create_windows_vps(config: Dict, user_id: str, server_id: str, namespace: st
|
||||
body=vm_body
|
||||
)
|
||||
|
||||
if progress_callback:
|
||||
progress_callback('Configuring network', 92, 'Setting up the Windows VPS network and ports.')
|
||||
|
||||
# Create Service with FIXED port handling
|
||||
if ports:
|
||||
service_ports = []
|
||||
@@ -1334,7 +1364,10 @@ def create_windows_vps(config: Dict, user_id: str, server_id: str, namespace: st
|
||||
ports.extend(actual_ports)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"⚠ Failed to create service: {e}")
|
||||
logger.error(f"Failed to create Windows VPS network service: {e}")
|
||||
if progress_callback:
|
||||
progress_callback('Failed', 92, 'Could not create the VPS network service.')
|
||||
raise RuntimeError(f'Failed to create Windows VPS network service: {e}') from e
|
||||
|
||||
server_data = {
|
||||
'id': server_id,
|
||||
@@ -1353,7 +1386,20 @@ def create_windows_vps(config: Dict, user_id: str, server_id: str, namespace: st
|
||||
'k8s_name': server_id
|
||||
}
|
||||
|
||||
return create_server_record(server_data)
|
||||
if progress_callback:
|
||||
progress_callback('Registering VPS', 97, 'Saving the VPS to your server list.')
|
||||
server = create_server_record(server_data)
|
||||
if not server:
|
||||
raise RuntimeError('VPS resources were created, but the server record could not be saved.')
|
||||
|
||||
if k8s_manager.connected:
|
||||
threading.Thread(
|
||||
target=wait_and_sync_vm_status,
|
||||
args=(server_id, user_id, namespace),
|
||||
daemon=True
|
||||
).start()
|
||||
|
||||
return server
|
||||
|
||||
def create_website_server(config: Dict, user_id: str, server_id: str, namespace: str) -> Dict:
|
||||
"""Create Website Server with FIXED port handling"""
|
||||
|
||||
Reference in new issue
Block a user