From 0f671c31dfa059448e47370a9b10d7e338b6ab9e Mon Sep 17 00:00:00 2001 From: Giacomo Marciani Date: Fri, 29 Nov 2024 12:04:50 +0100 Subject: [PATCH] [UNSTABLE] Upgrade Werkzeug, Connexion, Flask. Signed-off-by: Giacomo Marciani --- CHANGELOG.md | 3 +++ THIRD-PARTY-LICENSES.txt | 4 ++-- cli/requirements.txt | 6 +++--- cli/setup.py | 6 +++--- cli/src/pcluster/api/awslambda/serverless_wsgi.py | 14 +++++++------- cli/src/pcluster/api/encoder.py | 12 +++++++++--- cli/src/pcluster/api/flask_app.py | 8 +++++--- 7 files changed, 32 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6af609b313..14795a9c2f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,9 @@ CHANGELOG - web_viewer: `2024.0-18131-1` - Upgrade mysql-community-client to version 8.0.39. - Remove support for Python 3.7 and 3.8, which are in end of life. +- Upgrade Werkzeug to version 3.1.3. +- Upgrade Connexion to version 3.1.x. +- Upgrade Flask to version 3.1.0. **BUG FIXES** - When mounting an external OpenZFS, it is no longer required to set the outbound rules for ports 111, 2049, 20001, 20002, 20003. diff --git a/THIRD-PARTY-LICENSES.txt b/THIRD-PARTY-LICENSES.txt index 075dfec5e7..441258001d 100644 --- a/THIRD-PARTY-LICENSES.txt +++ b/THIRD-PARTY-LICENSES.txt @@ -209,7 +209,7 @@ ------ ** clickclick; version 20.10.2 -- https://codeberg.org/hjacobs/python-clickclick -** connexion; version 2.13.1 -- https://github.com/zalando/connexion +** connexion; version 3.1.0 -- https://github.com/zalando/connexion ** python-dateutil; version 2.8.2 -- https://github.com/dateutil/dateutil ** constructs; version 3.4.344 -- https://github.com/aws/constructs ** jsii; version 1.85.0 -- https://github.com/aws/jsii @@ -675,7 +675,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. Copyright 2007 Pallets ** Flask; version 2.2.5 -- https://palletsprojects.com/p/flask Copyright 2010 Pallets -** Werkzeug; version 2.3.8 -- https://pypi.org/project/Werkzeug/ +** Werkzeug; version 3.1.3 -- https://pypi.org/project/Werkzeug/ Copyright 2007 Pallets Redistribution and use in source and binary forms, with or without diff --git a/cli/requirements.txt b/cli/requirements.txt index dce6fedf37..59ee9b6d83 100644 --- a/cli/requirements.txt +++ b/cli/requirements.txt @@ -16,12 +16,12 @@ aws-cdk.core~=1.164 aws_cdk.aws-cloudwatch~=1.164 aws_cdk.aws-lambda~=1.164 boto3>=1.16.14 -connexion~=2.13.0 -flask>=2.2.5,<2.3 +connexion[flask]==3.1.0 +flask==3.1.0 jinja2~=3.0 jmespath~=0.10 jsii==1.85.0 marshmallow~=3.10 PyYAML>=5.3.1,!=5.4 tabulate>=0.8.8,<=0.8.10 -werkzeug~=2.0 +werkzeug==3.1.0 diff --git a/cli/setup.py b/cli/setup.py index 811965a5ae..d97b8b6aee 100644 --- a/cli/setup.py +++ b/cli/setup.py @@ -46,9 +46,9 @@ def readme(): "aws-cdk.aws-ssm~=" + CDK_VERSION, "aws-cdk.aws-sqs~=" + CDK_VERSION, "aws-cdk.aws-cloudformation~=" + CDK_VERSION, - "werkzeug~=2.0", - "connexion~=2.13.0", - "flask>=2.2.5,<2.3", + "werkzeug==3.1.0", + "connexion[flask]==3.1.0", + "flask==3.1.0", "jmespath~=0.10", "jsii==1.85.0", ] diff --git a/cli/src/pcluster/api/awslambda/serverless_wsgi.py b/cli/src/pcluster/api/awslambda/serverless_wsgi.py index 37bd594df5..668271b4c4 100644 --- a/cli/src/pcluster/api/awslambda/serverless_wsgi.py +++ b/cli/src/pcluster/api/awslambda/serverless_wsgi.py @@ -17,10 +17,10 @@ import json import os import sys +from urllib.parse import unquote, unquote_plus, urlencode from werkzeug.datastructures import Headers, MultiDict, iter_multi_items from werkzeug.http import HTTP_STATUS_CODES -from werkzeug.urls import url_encode, url_unquote, url_unquote_plus from werkzeug.wrappers import Response # List of MIME types that should not be base64 encoded. MIME types within `text/*` @@ -95,8 +95,8 @@ def encode_query_string(event): if not params: params = "" if is_alb_event(event): - params = MultiDict((url_unquote_plus(k), url_unquote_plus(v)) for k, v in iter_multi_items(params)) - return url_encode(params) + params = MultiDict((unquote_plus(k), unquote_plus(v)) for k, v in iter_multi_items(params)) + return urlencode(params) def get_script_name(headers, request_context): @@ -203,7 +203,7 @@ def handle_payload_v1(app, event, context): environ = { "CONTENT_LENGTH": str(len(body)), "CONTENT_TYPE": headers.get("Content-Type", ""), - "PATH_INFO": url_unquote(path_info), + "PATH_INFO": unquote(path_info), "QUERY_STRING": encode_query_string(event), "REMOTE_ADDR": event.get("requestContext", {}).get("identity", {}).get("sourceIp", ""), "REMOTE_USER": event.get("requestContext", {}).get("authorizer", {}).get("principalId", ""), @@ -247,7 +247,7 @@ def handle_payload_v2(app, event, context): environ = { "CONTENT_LENGTH": str(len(body)), "CONTENT_TYPE": headers.get("Content-Type", ""), - "PATH_INFO": url_unquote(path_info), + "PATH_INFO": unquote(path_info), "QUERY_STRING": event.get("rawQueryString", ""), "REMOTE_ADDR": event.get("requestContext", {}).get("http", {}).get("sourceIp", ""), "REMOTE_USER": event.get("requestContext", {}).get("authorizer", {}).get("principalId", ""), @@ -295,8 +295,8 @@ def handle_lambda_integration(app, event, context): environ = { "CONTENT_LENGTH": str(len(body)), "CONTENT_TYPE": headers.get("Content-Type", ""), - "PATH_INFO": url_unquote(path_info), - "QUERY_STRING": url_encode(event.get("query", {})), + "PATH_INFO": unquote(path_info), + "QUERY_STRING": urlencode(event.get("query", {})), "REMOTE_ADDR": event.get("identity", {}).get("sourceIp", ""), "REMOTE_USER": event.get("principalId", ""), "REQUEST_METHOD": event.get("method", ""), diff --git a/cli/src/pcluster/api/encoder.py b/cli/src/pcluster/api/encoder.py index 750d3d0a28..5797fffc4e 100644 --- a/cli/src/pcluster/api/encoder.py +++ b/cli/src/pcluster/api/encoder.py @@ -9,15 +9,17 @@ # Generated by OpenAPI Generator (python-flask) import datetime +import json import six -from connexion.apps.flask_app import FlaskJSONEncoder +from connexion import jsonifier +from connexion.jsonifier import Jsonifier from pcluster.api.models.base_model_ import Model from pcluster.utils import to_iso_timestr -class JSONEncoder(FlaskJSONEncoder): +class JSONEncoder(jsonifier.JSONEncoder): """Make the model objects JSON serializable.""" include_nulls = False @@ -35,4 +37,8 @@ def default(self, obj): # pylint: disable=arguments-renamed return dikt elif isinstance(obj, datetime.date): return to_iso_timestr(obj) - return FlaskJSONEncoder.default(self, obj) + return jsonifier.JSONEncoder.default(self, obj) + + @staticmethod + def jsonifier(): + return Jsonifier(json_=json, cls=JSONEncoder) diff --git a/cli/src/pcluster/api/flask_app.py b/cli/src/pcluster/api/flask_app.py index 0c1e23cca4..1a40214164 100644 --- a/cli/src/pcluster/api/flask_app.py +++ b/cli/src/pcluster/api/flask_app.py @@ -10,7 +10,7 @@ import connexion from connexion import ProblemException -from connexion.decorators.validation import ParameterValidator +from connexion.validators.parameter import ParameterValidator from flask import Response, jsonify, request from werkzeug.exceptions import HTTPException @@ -74,9 +74,10 @@ def __init__(self, swagger_ui: bool = False, validate_responses=False): assert_valid_node_js() options = {"swagger_ui": swagger_ui} - self.app = connexion.FlaskApp(__name__, specification_dir="openapi/", skip_error_handlers=True) + #TODO find a replacement for FlaskApp(skip_error_handlers=True) + self.app = connexion.FlaskApp(__name__, specification_dir="openapi/") self.flask_app = self.app.app - self.flask_app.json_encoder = encoder.JSONEncoder + # self.flask_app.json_encoder = encoder.JSONEncoder self.app.add_api( "openapi.yaml", arguments={"title": "ParallelCluster"}, @@ -84,6 +85,7 @@ def __init__(self, swagger_ui: bool = False, validate_responses=False): options=options, validate_responses=validate_responses, validator_map={"parameter": CustomParameterValidator}, + jsonifier=encoder.JSONEncoder.jsonifier() ) self.app.add_error_handler(HTTPException, self._handle_http_exception) self.app.add_error_handler(ProblemException, self._handle_problem_exception)