next_updates

This commit is contained in:
2025-12-28 05:52:49 +01:00
parent 986f3ae5b5
commit 6b254534f6
8 changed files with 766 additions and 533 deletions

View File

@@ -5,14 +5,15 @@ from app import db
import json
import subprocess
import os
import requests
import random
import string
def generate_remote_id(length=6):
return ''.join(random.choices(string.digits, k=length))
jobs_bp = Blueprint('jobs', __name__, url_prefix='/jobs')
@jobs_bp.route('/submit')
@login_required
def submit():
return render_template('jobs/submit.html')
@jobs_bp.route('/debug/test-step2')
@login_required
def debug_test_step2():
@@ -270,7 +271,7 @@ def submit_step2():
flash('Please select at least one scenario', 'error')
return redirect(url_for('jobs.submit'))
return render_template('jobs/submit_step3.html',
return render_template('jobs/submit_review.html',
branch_name=branch_name,
scenarios=selected_scenarios,
scenario_map=scenario_map)
@@ -318,50 +319,14 @@ def submit_step2_validated():
print(f"[ERROR] Step2 - Traceback: {traceback.format_exc()}")
flash(f'Error loading scenario selection: {str(e)}', 'error')
return redirect(url_for('jobs.submit'))
@login_required
def submit_step2():
branch_name = request.form.get('branch_name')
selected_scenarios = request.form.getlist('scenarios')
if not selected_scenarios:
flash('Please select at least one scenario', 'error')
return redirect(url_for('jobs.submit'))
return render_template('jobs/submit_step3.html',
branch_name=branch_name,
scenarios=selected_scenarios)
@jobs_bp.route('/submit/step3', methods=['POST'])
@login_required
def submit_step3():
branch_name = request.form.get('branch_name')
scenarios_json = request.form.get('scenarios')
scenario_map_json = request.form.get('scenario_map')
environment = request.form.get('environment')
try:
scenarios = json.loads(scenarios_json) if scenarios_json else []
scenario_map = json.loads(scenario_map_json) if scenario_map_json else {}
except json.JSONDecodeError:
flash('Invalid scenario data', 'error')
return redirect(url_for('jobs.submit'))
return render_template('jobs/submit_step4.html',
branch_name=branch_name,
scenarios=scenarios,
scenario_map=scenario_map,
environment=environment)
@jobs_bp.route('/submit/final', methods=['POST'])
@login_required
def submit_final():
branch_name = request.form.get('branch_name')
scenarios_json = request.form.get('scenarios')
scenario_map_json = request.form.get('scenario_map')
environment = request.form.get('environment')
test_mode = request.form.get('test_mode')
keep_devbenches = request.form.get('keep_devbenches') == 'on'
reuse_results = request.form.get('reuse_results') == 'on'
environment = request.form.get('environment', 'staging')
test_mode = request.form.get('test_mode', 'simulator')
try:
scenarios = json.loads(scenarios_json) if scenarios_json else []
@@ -370,22 +335,44 @@ def submit_final():
flash('Invalid scenario data', 'error')
return redirect(url_for('jobs.submit'))
# Generate Remote IDs
remote_queue_id = generate_remote_id()
remote_task_ids = {s: generate_remote_id() for s in scenarios}
# Prepare Remote Payload
# Format: {"source": "branch", "queue_id": ["staging", {"task_id": "path"}]}
payload = {
"source": branch_name,
remote_queue_id: [
environment,
{remote_task_ids[s]: scenario_map.get(s, s) for s in scenarios}
]
}
# Trigger Remote Queue
try:
remote_url = "http://asf-server.duckdns.org:8080/api/queue"
response = requests.post(remote_url, json=payload, timeout=10)
response.raise_for_status()
print(f"[DEBUG] Remote queue triggered: {response.json()}")
except Exception as e:
print(f"[ERROR] Failed to trigger remote queue: {e}")
flash(f'Warning: Job saved but failed to trigger remote queue: {str(e)}', 'warning')
job = Job(
user_id=current_user.id,
branch_name=branch_name,
scenarios=json.dumps(scenarios), # Store as JSON string
scenarios=json.dumps(scenarios),
environment=environment,
test_mode=test_mode,
keep_devbenches=keep_devbenches,
reuse_results=reuse_results,
status='in_progress'
status='waiting', # Start with waiting (sand watch)
remote_queue_id=remote_queue_id,
remote_task_ids=json.dumps(remote_task_ids)
)
db.session.add(job)
db.session.commit()
# TODO: Start test execution in background using scenario_map
flash('Test job submitted successfully', 'success')
return redirect(url_for('dashboard.index'))
@@ -409,23 +396,96 @@ def view_job(job_id):
'submitted_at': job.submitted_at.strftime('%Y-%m-%d %H:%M:%S'),
'completed_at': job.completed_at.strftime('%Y-%m-%d %H:%M:%S') if job.completed_at else None,
'duration': job.duration,
'keep_devbenches': job.keep_devbenches,
'reuse_results': job.reuse_results,
'results_path': job.results_path
'remote_queue_id': job.remote_queue_id,
'remote_task_ids': job.remote_task_ids,
'remote_results': job.remote_results,
'queue_log': job.queue_log
})
@jobs_bp.route('/<int:job_id>/abort', methods=['POST'])
@login_required
def abort_job(job_id):
return jsonify({'error': 'Abort functionality is not implemented yet'}), 400
@jobs_bp.route('/<int:job_id>/status')
@login_required
def get_job_status(job_id):
job = Job.query.get_or_404(job_id)
if not current_user.is_admin and job.user_id != current_user.id:
return jsonify({'error': 'Access denied'}), 403
if job.status == 'in_progress':
job.status = 'aborted'
db.session.commit()
# TODO: Kill the running process
return jsonify({'success': True})
return jsonify({'error': 'Job is not in progress'}), 400
if job.status not in ['passed', 'failed', 'aborted']:
# Poll remote API
try:
# 1. Check Queue Status
status_url = f"http://asf-server.duckdns.org:8080/api/status/{job.remote_queue_id}"
q_resp = requests.get(status_url, timeout=5)
if q_resp.status_code == 200:
q_data = q_resp.json()
remote_status = q_data.get('status', '').lower()
if remote_status == 'running':
job.status = 'in_progress'
elif remote_status == 'waiting':
job.status = 'waiting'
# 2. Fetch Queue Logs if running
if remote_status == 'running':
log_url = f"http://asf-server.duckdns.org:8080/results/{job.remote_queue_id}/queue_log.txt"
l_resp = requests.get(log_url, timeout=5)
if l_resp.status_code == 200:
job.queue_log = l_resp.text
# 3. Check Task Statuses and Results
task_ids = json.loads(job.remote_task_ids) if job.remote_task_ids else {}
results = json.loads(job.remote_results) if job.remote_results else {}
all_finished = True
any_failed = False
for scenario, task_id in task_ids.items():
# If we don't have result yet, check it
if scenario not in results:
# Check task status
t_status_url = f"http://asf-server.duckdns.org:8080/api/status/{task_id}"
t_resp = requests.get(t_status_url, timeout=2)
if t_resp.status_code == 200:
t_data = t_resp.json()
if t_data.get('status') == 'Finished':
# Fetch results
res_url = f"http://asf-server.duckdns.org:8080/results/{job.remote_queue_id}/{task_id}/final_summary.json"
r_resp = requests.get(res_url, timeout=2)
if r_resp.status_code == 200:
r_data = r_resp.json()
# User says: {"SCENARIO_NAME": ["PASS/FAIL", "link"]}
# We need to find the key that matches our scenario (case insensitive maybe?)
for key, val in r_data.items():
if key.upper() == scenario.upper():
results[scenario] = val
if val[0] == 'FAIL':
any_failed = True
break
else:
all_finished = False
else:
all_finished = False
else:
all_finished = False
else:
if results[scenario][0] == 'FAIL':
any_failed = True
if all_finished and task_ids:
job.status = 'failed' if any_failed else 'passed'
job.completed_at = db.session.query(db.func.now()).scalar()
job.remote_results = json.dumps(results)
db.session.commit()
except Exception as e:
print(f"[ERROR] Status polling failed: {e}")
return jsonify({
'status': job.status,
'status_icon': job.get_status_icon(),
'remote_results': job.remote_results,
'queue_log': job.queue_log
})