def get_function_target(target):
    """Get the configured function target."""
    target = target or os.environ.get("FUNCTION_TARGET", "")
    # Set the environment variable if it wasn't already
    os.environ["FUNCTION_TARGET"] = target
    if not target:
        raise InvalidConfigurationException(
            "Target is not specified (FUNCTION_TARGET environment variable not set)"
        )
    return target
Esempio n. 2
0
def create_app(target=None, source=None, signature_type=None):
    # Get the configured function target
    target = target or os.environ.get("FUNCTION_TARGET", "")
    # Set the environment variable if it wasn't already
    os.environ["FUNCTION_TARGET"] = target

    if not target:
        raise InvalidConfigurationException(
            "Target is not specified (FUNCTION_TARGET environment variable not set)"
        )

    # Get the configured function source
    source = source or os.environ.get("FUNCTION_SOURCE", DEFAULT_SOURCE)

    # Python 3.5: os.path.exist does not support PosixPath
    source = str(source)

    # Set the template folder relative to the source path
    # Python 3.5: join does not support PosixPath
    template_folder = str(pathlib.Path(source).parent / "templates")

    if not os.path.exists(source):
        raise MissingSourceException(
            "File {source} that is expected to define function doesn't exist".format(
                source=source
            )
        )

    # Get the configured function signature type
    signature_type = signature_type or os.environ.get(
        "FUNCTION_SIGNATURE_TYPE", DEFAULT_SIGNATURE_TYPE
    )
    # Set the environment variable if it wasn't already
    os.environ["FUNCTION_SIGNATURE_TYPE"] = signature_type

    # Load the source file:
    # 1. Extract the module name from the source path
    realpath = os.path.realpath(source)
    directory, filename = os.path.split(realpath)
    name, extension = os.path.splitext(filename)

    # 2. Create a new module
    spec = importlib.util.spec_from_file_location(name, realpath)
    source_module = importlib.util.module_from_spec(spec)

    # 3. Add the directory of the source to sys.path to allow the function to
    # load modules relative to its location
    sys.path.append(directory)

    # 4. Add the module to sys.modules
    sys.modules[name] = source_module

    # 5. Create the application
    app = flask.Flask(target, template_folder=template_folder)
    app.config["MAX_CONTENT_LENGTH"] = MAX_CONTENT_LENGTH
    app.register_error_handler(500, crash_handler)
    global errorhandler
    errorhandler = app.errorhandler

    # 6. Execute the module, within the application context
    with app.app_context():
        spec.loader.exec_module(source_module)

    # Handle legacy GCF Python 3.7 behavior
    if os.environ.get("ENTRY_POINT"):
        os.environ["FUNCTION_TRIGGER_TYPE"] = signature_type
        os.environ["FUNCTION_NAME"] = os.environ.get("K_SERVICE", target)
        app.make_response_original = app.make_response

        def handle_none(rv):
            if rv is None:
                rv = "OK"
            return app.make_response_original(rv)

        app.make_response = handle_none

        # Handle log severity backwards compatibility
        import logging  # isort:skip

        logging.info = _LoggingHandler("INFO", sys.stderr).write
        logging.warn = _LoggingHandler("ERROR", sys.stderr).write
        logging.warning = _LoggingHandler("ERROR", sys.stderr).write
        logging.error = _LoggingHandler("ERROR", sys.stderr).write
        logging.critical = _LoggingHandler("ERROR", sys.stderr).write
        sys.stdout = _LoggingHandler("INFO", sys.stderr)
        sys.stderr = _LoggingHandler("ERROR", sys.stderr)

    # Extract the target function from the source file
    if not hasattr(source_module, target):
        raise MissingTargetException(
            "File {source} is expected to contain a function named {target}".format(
                source=source, target=target
            )
        )
    function = getattr(source_module, target)

    # Check that it is a function
    if not isinstance(function, types.FunctionType):
        raise InvalidTargetTypeException(
            "The function defined in file {source} as {target} needs to be of "
            "type function. Got: invalid type {target_type}".format(
                source=source, target=target, target_type=type(function)
            )
        )

    # Mount the function at the root. Support GCF's default path behavior
    # Modify the url_map and view_functions directly here instead of using
    # add_url_rule in order to create endpoints that route all methods
    if signature_type == "http":
        app.url_map.add(
            werkzeug.routing.Rule("/", defaults={"path": ""}, endpoint="run")
        )
        app.url_map.add(werkzeug.routing.Rule("/robots.txt", endpoint="error"))
        app.url_map.add(werkzeug.routing.Rule("/favicon.ico", endpoint="error"))
        app.url_map.add(werkzeug.routing.Rule("/<path:path>", endpoint="run"))
        app.view_functions["run"] = _http_view_func_wrapper(function, flask.request)
        app.view_functions["error"] = lambda: flask.abort(404, description="Not Found")
        app.after_request(read_request)
    elif signature_type == "event":
        app.url_map.add(
            werkzeug.routing.Rule(
                "/", defaults={"path": ""}, endpoint="run", methods=["POST"]
            )
        )
        app.url_map.add(
            werkzeug.routing.Rule("/<path:path>", endpoint="run", methods=["POST"])
        )
        app.view_functions["run"] = _event_view_func_wrapper(function, flask.request)
        # Add a dummy endpoint for GET /
        app.url_map.add(werkzeug.routing.Rule("/", endpoint="get", methods=["GET"]))
        app.view_functions["get"] = lambda: ""
    elif signature_type == "cloudevent":
        app.url_map.add(
            werkzeug.routing.Rule(
                "/", defaults={"path": ""}, endpoint=signature_type, methods=["POST"]
            )
        )
        app.url_map.add(
            werkzeug.routing.Rule(
                "/<path:path>", endpoint=signature_type, methods=["POST"]
            )
        )

        app.view_functions[signature_type] = _cloudevent_view_func_wrapper(
            function, flask.request
        )
    else:
        raise FunctionsFrameworkException(
            "Invalid signature type: {signature_type}".format(
                signature_type=signature_type
            )
        )

    return app
Esempio n. 3
0
def create_app(target=None, source=None, signature_type=None):
    # Get the configured function target
    target = target or os.environ.get("FUNCTION_TARGET", "")
    # Set the environment variable if it wasn't already
    os.environ["FUNCTION_TARGET"] = target

    if not target:
        raise InvalidConfigurationException(
            "Target is not specified (FUNCTION_TARGET environment variable not set)"
        )

    # Get the configured function source
    source = source or os.environ.get("FUNCTION_SOURCE", DEFAULT_SOURCE)

    # Python 3.5: os.path.exist does not support PosixPath
    source = str(source)

    # Set the template folder relative to the source path
    # Python 3.5: join does not support PosixPath
    template_folder = str(pathlib.Path(source).parent / "templates")

    if not os.path.exists(source):
        raise MissingSourceException(
            "File {source} that is expected to define function doesn't exist".
            format(source=source))

    # Get the configured function signature type
    signature_type = signature_type or os.environ.get(
        "FUNCTION_SIGNATURE_TYPE", DEFAULT_SIGNATURE_TYPE)
    # Set the environment variable if it wasn't already
    os.environ["FUNCTION_SIGNATURE_TYPE"] = signature_type

    # Load the source file
    spec = importlib.util.spec_from_file_location("main", source)
    source_module = importlib.util.module_from_spec(spec)
    sys.path.append(os.path.dirname(os.path.realpath(source)))
    spec.loader.exec_module(source_module)

    app = flask.Flask(target, template_folder=template_folder)

    # Extract the target function from the source file
    try:
        function = getattr(source_module, target)
    except AttributeError:
        raise MissingTargetException(
            "File {source} is expected to contain a function named {target}".
            format(source=source, target=target))

    # Check that it is a function
    if not isinstance(function, types.FunctionType):
        raise InvalidTargetTypeException(
            "The function defined in file {source} as {target} needs to be of "
            "type function. Got: invalid type {target_type}".format(
                source=source, target=target, target_type=type(function)))

    # Mount the function at the root. Support GCF's default path behavior
    # Modify the url_map and view_functions directly here instead of using
    # add_url_rule in order to create endpoints that route all methods
    if signature_type == "http":
        app.url_map.add(
            werkzeug.routing.Rule("/", defaults={"path": ""}, endpoint="run"))
        app.url_map.add(werkzeug.routing.Rule("/<path:path>", endpoint="run"))
        app.view_functions["run"] = _http_view_func_wrapper(
            function, flask.request)
    elif signature_type == "event":
        app.url_map.add(
            werkzeug.routing.Rule("/",
                                  defaults={"path": ""},
                                  endpoint="run",
                                  methods=["POST"]))
        app.url_map.add(
            werkzeug.routing.Rule("/<path:path>",
                                  endpoint="run",
                                  methods=["POST"]))
        app.view_functions["run"] = _event_view_func_wrapper(
            function, flask.request)
        # Add a dummy endpoint for GET /
        app.url_map.add(
            werkzeug.routing.Rule("/", endpoint="get", methods=["GET"]))
        app.view_functions["get"] = lambda: ""
    else:
        raise FunctionsFrameworkException(
            "Invalid signature type: {signature_type}".format(
                signature_type=signature_type))

    return app
Esempio n. 4
0
def create_app(target=None, source=None, signature_type=None):
    # Get the configured function target
    target = target or os.environ.get("FUNCTION_TARGET", "")
    # Set the environment variable if it wasn't already
    os.environ["FUNCTION_TARGET"] = target

    if not target:
        raise InvalidConfigurationException(
            "Target is not specified (FUNCTION_TARGET environment variable not set)"
        )

    # Get the configured function source
    source = source or os.environ.get("FUNCTION_SOURCE", DEFAULT_SOURCE)

    # Python 3.5: os.path.exist does not support PosixPath
    source = str(source)

    # Set the template folder relative to the source path
    # Python 3.5: join does not support PosixPath
    template_folder = str(pathlib.Path(source).parent / "templates")

    if not os.path.exists(source):
        raise MissingSourceException(
            "File {source} that is expected to define function doesn't exist".
            format(source=source))

    # Get the configured function signature type
    signature_type = signature_type or os.environ.get(
        "FUNCTION_SIGNATURE_TYPE", DEFAULT_SIGNATURE_TYPE)
    # Set the environment variable if it wasn't already
    os.environ["FUNCTION_SIGNATURE_TYPE"] = signature_type

    # Load the source file:
    # 1. Extract the module name from the source path
    realpath = os.path.realpath(source)
    directory, filename = os.path.split(realpath)
    name, extension = os.path.splitext(filename)

    # 2. Create a new module
    spec = importlib.util.spec_from_file_location(name, realpath)
    source_module = importlib.util.module_from_spec(spec)

    # 3. Add the directory of the source to sys.path to allow the function to
    # load modules relative to its location
    sys.path.append(directory)

    # 4. Add the module to sys.modules
    sys.modules[name] = source_module

    # 5. Execute the module
    spec.loader.exec_module(source_module)

    app = flask.Flask(target, template_folder=template_folder)
    app.config["MAX_CONTENT_LENGTH"] = MAX_CONTENT_LENGTH

    # Extract the target function from the source file
    try:
        function = getattr(source_module, target)
    except AttributeError:
        raise MissingTargetException(
            "File {source} is expected to contain a function named {target}".
            format(source=source, target=target))

    # Check that it is a function
    if not isinstance(function, types.FunctionType):
        raise InvalidTargetTypeException(
            "The function defined in file {source} as {target} needs to be of "
            "type function. Got: invalid type {target_type}".format(
                source=source, target=target, target_type=type(function)))

    # Mount the function at the root. Support GCF's default path behavior
    # Modify the url_map and view_functions directly here instead of using
    # add_url_rule in order to create endpoints that route all methods
    if signature_type == "http":
        app.url_map.add(
            werkzeug.routing.Rule("/", defaults={"path": ""}, endpoint="run"))
        app.url_map.add(werkzeug.routing.Rule("/robots.txt", endpoint="error"))
        app.url_map.add(werkzeug.routing.Rule("/favicon.ico",
                                              endpoint="error"))
        app.url_map.add(werkzeug.routing.Rule("/<path:path>", endpoint="run"))
        app.view_functions["run"] = _http_view_func_wrapper(
            function, flask.request)
        app.view_functions["error"] = lambda: flask.abort(
            404, description="Not Found")
        app.after_request(read_request)
    elif signature_type == "event" or signature_type == "cloudevent":
        app.url_map.add(
            werkzeug.routing.Rule("/",
                                  defaults={"path": ""},
                                  endpoint=signature_type,
                                  methods=["POST"]))
        app.url_map.add(
            werkzeug.routing.Rule("/<path:path>",
                                  endpoint=signature_type,
                                  methods=["POST"]))

        # Add a dummy endpoint for GET /
        app.url_map.add(
            werkzeug.routing.Rule("/", endpoint="get", methods=["GET"]))
        app.view_functions["get"] = lambda: ""

        # Add the view functions
        app.view_functions["event"] = _event_view_func_wrapper(
            function, flask.request)
        app.view_functions["cloudevent"] = _cloudevent_view_func_wrapper(
            function, flask.request)
    else:
        raise FunctionsFrameworkException(
            "Invalid signature type: {signature_type}".format(
                signature_type=signature_type))

    return app