Search code examples
pythonflaskjenkinspytestport

Threaded Flask app gives OSError: [Errno 98] Address already in use in Jenkins job


I am starting my Flask app in the thread and when I test it in local environment it works perfectly but whenever I run on Jenkins it gives an already in use error.

Here is my main test application to start in jenkins pipeline

python3.8 -m pytest -n 1 --reruns 1 -v -m Smoke --tb=short --instafail --durations=0

then inside the pytest_collection_finish i use

flask_thread = threading.Thread(target=run_flask_app)
flask_thread.daemon = True
flask_thread.start()
wait_for_trigger()

and here is my flask codes

from flask import Flask, request
from threading import Event
from werkzeug.serving import run_simple
import logging

app = Flask(__name__)
test_ready_event = Event()
logging.basicConfig(filename='flask_thread.log', level=logging.DEBUG,
                    format='%(asctime)s - %(levelname)s - %(message)s')


@app.route('/trigger', methods=['GET'])
def trigger_test_continue():
    test_ready_event.set()
    print("Trigger received. Signalling pytest to continue.")
    logging.info("Trigger received. Signalling pytest to continue.")
    return "Continuing tests.", 200


def run_flask_app():
    logging.info("Starting Flask app.")
    run_simple('0.0.0.0', 5055, app, use_reloader=False, use_debugger=True)


def stop_flask_app():
    func = request.environ.get('werkzeug.server.shutdown')
    if func is None:
        raise RuntimeError('Not running with the Werkzeug Server')
    func()
    logging.info("Flask app stopped.")


def wait_for_trigger():
    print("Waiting for the trigger to continue tests...")
    logging.info("Waiting for the trigger to continue tests...")
    test_ready_event.wait()
    print("Trigger received. Continuing with tests.")
    logging.info("Trigger received. Continuing with tests.")

Error

Exception in thread Thread-1:
Traceback (most recent call last):
  File "/usr/lib/python3.8/threading.py", line 932, in _bootstrap_inner
    self.run()
  File "/usr/lib/python3.8/threading.py", line 870, in run
    self._target(*self._args, **self._kwargs)
  File "/var/lib/path/execution_stopper.py", line 22, in run_flask_app
    run_simple('0.0.0.0', 5055, app, use_reloader=False, use_debugger=True)
  File "/usr/local/lib/python3.8/dist-packages/werkzeug/serving.py", line 1052, in run_simple
    inner()
  File "/usr/local/lib/python3.8/dist-packages/werkzeug/serving.py", line 996, in inner
    srv = make_server(
  File "/usr/local/lib/python3.8/dist-packages/werkzeug/serving.py", line 862, in make_server
    return BaseWSGIServer(
  File "/usr/local/lib/python3.8/dist-packages/werkzeug/serving.py", line 740, in __init__
    HTTPServer.__init__(self, server_address, handler)
  File "/usr/lib/python3.8/socketserver.py", line 452, in __init__
    self.server_bind()
  File "/usr/lib/python3.8/http/server.py", line 138, in server_bind
    socketserver.TCPServer.server_bind(self)
  File "/usr/lib/python3.8/socketserver.py", line 466, in server_bind
    self.socket.bind(self.server_address)
OSError: [Errno 98] Address already in use

when i run this in jenkins machine

sudo netstat -tulpn | grep :5005

gives

tcp        0      0 0.0.0.0:5005            0.0.0.0:*               LISTEN      1636790/python3.8

Solution

  • For the ones who has faced same issue i simply changed my flask code to this

    app = Flask(__name__)
    logging.basicConfig(filename='flask_thread.log', level=logging.INFO,
                        format='%(asctime)s - %(levelname)s - %(message)s')
    
    @app.route('/trigger', methods=['GET'])
    def trigger_test_continue():
        logging.info("Trigger received. Signalling pytest to continue.")
        stop_flask_app()
        return jsonify({'status': 'success', 'message': 'Continuing test execution'}), 200
    
    
    def run_flask_app():
        logging.info("Starting Flask app.")
        app.run(host='0.0.0.0', port=5005, debug=False, use_reloader=False)
    
    
    def stop_flask_app():
        func = request.environ.get('werkzeug.server.shutdown')
        if func is None:
            raise RuntimeError('Not running with the Werkzeug Server')
        func()
        logging.info("Flask app stopped.")
    

    and in the main application

    flask_thread = threading.Thread(target=run_flask_app)
    flask_thread.daemon = True
    flask_thread.start()
    flask_thread.join()