much improved launch process

This commit is contained in:
2025-06-23 14:50:37 +02:00
parent 033f82eb2b
commit 40b1a63cf5
4 changed files with 225 additions and 74 deletions

View File

@@ -214,7 +214,6 @@ def list_gitea_repos():
return jsonify({'message': 'Missing required fields'}), 400
try:
# Try different authentication methods
headers = {
'Accept': 'application/json'
}
@@ -789,6 +788,9 @@ def deploy_stack():
if not portainer_settings:
return jsonify({'error': 'Portainer settings not configured'}), 400
# Define timeout early to ensure it's available throughout the function
stack_timeout = current_app.config.get('STACK_DEPLOYMENT_TIMEOUT', 300) # Default to 5 minutes
# Verify Portainer authentication
auth_response = requests.get(
f"{portainer_settings['url'].rstrip('/')}/api/status",
@@ -830,6 +832,7 @@ def deploy_stack():
# Log the request data
current_app.logger.info(f"Creating stack with data: {json.dumps(data)}")
current_app.logger.info(f"Using endpoint ID: {endpoint_id}")
current_app.logger.info(f"Using timeout: {stack_timeout} seconds")
# First, check if a stack with this name already exists
stacks_url = f"{portainer_settings['url'].rstrip('/')}/api/stacks"
@@ -867,7 +870,7 @@ def deploy_stack():
# Add endpointId as a query parameter
params = {'endpointId': endpoint_id}
# Use a shorter timeout for stack creation initiation (2 minutes)
# Use a configurable timeout for stack creation initiation
create_response = requests.post(
url,
headers={
@@ -877,7 +880,7 @@ def deploy_stack():
},
params=params,
json=request_body,
timeout=120 # 2 minutes timeout for stack creation initiation
timeout=stack_timeout # Use configurable timeout
)
# Log the response details
@@ -909,8 +912,14 @@ def deploy_stack():
})
except requests.exceptions.Timeout:
current_app.logger.error("Request timed out while initiating stack deployment")
return jsonify({'error': 'Request timed out while initiating stack deployment. The operation may still be in progress.'}), 504
current_app.logger.error(f"Request timed out after {stack_timeout} seconds while initiating stack deployment")
current_app.logger.error(f"Stack name: {data.get('name', 'unknown') if 'data' in locals() else 'unknown'}")
current_app.logger.error(f"Portainer URL: {portainer_settings.get('url', 'unknown') if 'portainer_settings' in locals() else 'unknown'}")
return jsonify({
'error': f'Request timed out after {stack_timeout} seconds while initiating stack deployment. The operation may still be in progress.',
'timeout_seconds': stack_timeout,
'stack_name': data.get('name', 'unknown') if 'data' in locals() else 'unknown'
}), 504
except Exception as e:
current_app.logger.error(f"Error deploying stack: {str(e)}")
return jsonify({'error': str(e)}), 500
@@ -975,48 +984,74 @@ def check_stack_status():
# Get stack services to check their status
services_url = f"{portainer_settings['url'].rstrip('/')}/api/endpoints/{endpoint_id}/docker/services"
services_response = requests.get(
services_url,
headers={
'X-API-Key': portainer_settings['api_key'],
'Accept': 'application/json'
},
params={'filters': json.dumps({'label': f'com.docker.stack.namespace={data["stack_name"]}'})},
timeout=30
)
if not services_response.ok:
return jsonify({'error': 'Failed to get stack services'}), 500
services = services_response.json()
current_app.logger.info(f"Checking services for stack {data['stack_name']} at endpoint {endpoint_id}")
# Check if all services are running
all_running = True
service_statuses = []
for service in services:
replicas_running = service.get('Spec', {}).get('Mode', {}).get('Replicated', {}).get('Replicas', 0)
replicas_actual = service.get('ServiceStatus', {}).get('RunningTasks', 0)
service_status = {
'name': service.get('Spec', {}).get('Name', 'Unknown'),
'replicas_expected': replicas_running,
'replicas_running': replicas_actual,
'status': 'running' if replicas_actual >= replicas_running else 'not_running'
}
service_statuses.append(service_status)
if replicas_actual < replicas_running:
all_running = False
try:
services_response = requests.get(
services_url,
headers={
'X-API-Key': portainer_settings['api_key'],
'Accept': 'application/json'
},
params={'filters': json.dumps({'label': f'com.docker.stack.namespace={data["stack_name"]}'})},
timeout=30
)
# Determine overall stack status
if all_running and len(services) > 0:
status = 'active'
elif len(services) > 0:
status = 'partial'
else:
status = 'inactive'
current_app.logger.info(f"Services API response status: {services_response.status_code}")
if services_response.ok:
services = services_response.json()
current_app.logger.info(f"Found {len(services)} services for stack {data['stack_name']}")
# Check if all services are running
all_running = True
service_statuses = []
for service in services:
replicas_running = service.get('Spec', {}).get('Mode', {}).get('Replicated', {}).get('Replicas', 0)
replicas_actual = service.get('ServiceStatus', {}).get('RunningTasks', 0)
service_status = {
'name': service.get('Spec', {}).get('Name', 'Unknown'),
'replicas_expected': replicas_running,
'replicas_running': replicas_actual,
'status': 'running' if replicas_actual >= replicas_running else 'not_running'
}
service_statuses.append(service_status)
if replicas_actual < replicas_running:
all_running = False
# Determine overall stack status
if all_running and len(services) > 0:
status = 'active'
elif len(services) > 0:
status = 'partial'
else:
status = 'inactive'
else:
# Services API failed, but stack exists - assume it's still starting up
current_app.logger.warning(f"Failed to get services for stack {data['stack_name']}: {services_response.status_code} - {services_response.text}")
# Provide more specific error context
if services_response.status_code == 404:
current_app.logger.info(f"Services endpoint not found for stack {data['stack_name']} - stack may still be initializing")
elif services_response.status_code == 403:
current_app.logger.warning(f"Access denied to services for stack {data['stack_name']} - check Portainer permissions")
elif services_response.status_code >= 500:
current_app.logger.warning(f"Portainer server error when getting services for stack {data['stack_name']}")
services = []
service_statuses = []
status = 'starting' # Stack exists but services not available yet
except Exception as e:
# Exception occurred while getting services, but stack exists
current_app.logger.warning(f"Exception getting services for stack {data['stack_name']}: {str(e)}")
services = []
service_statuses = []
status = 'starting' # Stack exists but services not available yet
return jsonify({
'success': True,
@@ -1026,7 +1061,9 @@ def check_stack_status():
'status': status,
'services': service_statuses,
'total_services': len(services),
'running_services': len([s for s in service_statuses if s['status'] == 'running'])
'running_services': len([s for s in service_statuses if s['status'] == 'running']),
'stack_created_at': target_stack.get('CreatedAt', 'unknown'),
'stack_updated_at': target_stack.get('UpdatedAt', 'unknown')
}
})