deploying works!
This commit is contained in:
@@ -7,6 +7,7 @@ from datetime import datetime
|
||||
import requests
|
||||
import base64
|
||||
from routes.admin_api import token_required
|
||||
import json
|
||||
|
||||
launch_api = Blueprint('launch_api', __name__)
|
||||
|
||||
@@ -709,4 +710,135 @@ def download_docker_compose():
|
||||
|
||||
except Exception as e:
|
||||
current_app.logger.error(f"Error downloading docker-compose.yml: {str(e)}")
|
||||
return jsonify({'message': f'Error downloading docker-compose.yml: {str(e)}'}), 500
|
||||
return jsonify({'message': f'Error downloading docker-compose.yml: {str(e)}'}), 500
|
||||
|
||||
@launch_api.route('/deploy-stack', methods=['POST'])
|
||||
@csrf.exempt
|
||||
def deploy_stack():
|
||||
try:
|
||||
data = request.get_json()
|
||||
if not data or 'name' not in data or 'StackFileContent' not in data:
|
||||
return jsonify({'error': 'Missing required fields'}), 400
|
||||
|
||||
# Get Portainer settings
|
||||
portainer_settings = KeyValueSettings.get_value('portainer_settings')
|
||||
if not portainer_settings:
|
||||
return jsonify({'error': 'Portainer settings not configured'}), 400
|
||||
|
||||
# Verify Portainer authentication
|
||||
auth_response = requests.get(
|
||||
f"{portainer_settings['url'].rstrip('/')}/api/status",
|
||||
headers={
|
||||
'X-API-Key': portainer_settings['api_key'],
|
||||
'Accept': 'application/json'
|
||||
},
|
||||
timeout=30 # 30 seconds timeout for status check
|
||||
)
|
||||
|
||||
if not auth_response.ok:
|
||||
current_app.logger.error(f"Portainer authentication failed: {auth_response.text}")
|
||||
return jsonify({'error': 'Failed to authenticate with Portainer'}), 401
|
||||
|
||||
# Get Portainer endpoint ID (assuming it's the first endpoint)
|
||||
endpoint_response = requests.get(
|
||||
f"{portainer_settings['url'].rstrip('/')}/api/endpoints",
|
||||
headers={
|
||||
'X-API-Key': portainer_settings['api_key'],
|
||||
'Accept': 'application/json'
|
||||
},
|
||||
timeout=30 # 30 seconds timeout for endpoint check
|
||||
)
|
||||
if not endpoint_response.ok:
|
||||
error_text = endpoint_response.text
|
||||
try:
|
||||
error_json = endpoint_response.json()
|
||||
error_text = error_json.get('message', error_text)
|
||||
except:
|
||||
pass
|
||||
return jsonify({'error': f'Failed to get Portainer endpoints: {error_text}'}), 500
|
||||
|
||||
endpoints = endpoint_response.json()
|
||||
if not endpoints:
|
||||
return jsonify({'error': 'No Portainer endpoints found'}), 400
|
||||
|
||||
endpoint_id = endpoints[0]['Id']
|
||||
|
||||
# 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}")
|
||||
|
||||
# First, check if a stack with this name already exists
|
||||
stacks_url = f"{portainer_settings['url'].rstrip('/')}/api/stacks"
|
||||
stacks_response = requests.get(
|
||||
stacks_url,
|
||||
headers={
|
||||
'X-API-Key': portainer_settings['api_key'],
|
||||
'Accept': 'application/json'
|
||||
},
|
||||
params={'filters': json.dumps({'Name': data['name']})},
|
||||
timeout=30
|
||||
)
|
||||
|
||||
if stacks_response.ok:
|
||||
existing_stacks = stacks_response.json()
|
||||
for stack in existing_stacks:
|
||||
if stack['Name'] == data['name']:
|
||||
current_app.logger.info(f"Found existing stack: {stack['Name']} (ID: {stack['Id']})")
|
||||
return jsonify({
|
||||
'name': stack['Name'],
|
||||
'id': stack['Id'],
|
||||
'status': 'existing'
|
||||
})
|
||||
|
||||
# If no existing stack found, proceed with creation
|
||||
url = f"{portainer_settings['url'].rstrip('/')}/api/stacks/create/standalone/string"
|
||||
current_app.logger.info(f"Making request to: {url}")
|
||||
|
||||
# Prepare the request body according to Portainer's API spec
|
||||
request_body = data
|
||||
|
||||
# Add endpointId as a query parameter
|
||||
params = {'endpointId': endpoint_id}
|
||||
|
||||
# Set a longer timeout for stack creation (10 minutes)
|
||||
create_response = requests.post(
|
||||
url,
|
||||
headers={
|
||||
'X-API-Key': portainer_settings['api_key'],
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json'
|
||||
},
|
||||
params=params,
|
||||
json=request_body,
|
||||
timeout=600 # 10 minutes timeout for stack creation
|
||||
)
|
||||
|
||||
# Log the response details
|
||||
current_app.logger.info(f"Response status: {create_response.status_code}")
|
||||
current_app.logger.info(f"Response headers: {dict(create_response.headers)}")
|
||||
|
||||
response_text = create_response.text
|
||||
current_app.logger.info(f"Response body: {response_text}")
|
||||
|
||||
if not create_response.ok:
|
||||
error_message = response_text
|
||||
try:
|
||||
error_json = create_response.json()
|
||||
error_message = error_json.get('message', error_message)
|
||||
except:
|
||||
pass
|
||||
return jsonify({'error': f'Failed to create stack: {error_message}'}), 500
|
||||
|
||||
stack_info = create_response.json()
|
||||
return jsonify({
|
||||
'name': stack_info['Name'],
|
||||
'id': stack_info['Id'],
|
||||
'status': 'created'
|
||||
})
|
||||
|
||||
except requests.exceptions.Timeout:
|
||||
current_app.logger.error("Request timed out while deploying stack")
|
||||
return jsonify({'error': 'Request timed out while deploying stack. The operation may still be in progress.'}), 504
|
||||
except Exception as e:
|
||||
current_app.logger.error(f"Error deploying stack: {str(e)}")
|
||||
return jsonify({'error': str(e)}), 500
|
||||
Reference in New Issue
Block a user