1

I'm having difficulty with my Cloud Function in GCP that is simply supposed to return the raw XML stored in a GCS Bucket when invoked with a basic GET request. It works fine without any type of authentication, however since I added the Flask-HTTPAuth package to the mix in order to add some measure of security before exposing the endpoint, the application deploys fine, but crashes without any sort of hint as to why as soon as it is invoked. The error in SD Logging is as follows:

severity: "DEBUG"  
textPayload: "Function execution took 1847 ms, finished with status: 'crash'"  
timestamp: "2020-07-15T17:22:15.158036700Z"

The function in question (anonymized):

from flask import Flask, request, jsonify, make_response, abort
from flask_httpauth import HTTPBasicAuth
from google.cloud import storage, secretmanager
import google.cloud.logging
import logging
import sys

app = Flask(__name__)
auth = HTTPBasicAuth()

PROJECT_ID = 'example_project'
GCS_BUCKET = 'example_bucket'
users = ['example_user']

# Instantiate logger
client = google.cloud.logging.Client()
client.get_default_handler()
client.setup_logging()

@auth.verify_password
def verify_password(username, password):
    # Instantiate the Secret Manager client.
    sm_client = secretmanager.SecretManagerServiceClient()

    # Load secrets
    name = sm_client.secret_version_path(PROJECT_ID, 'example_secrets_ref', 1)
    secrets_pass = sm_client.access_secret_version(name)
    passwords = [secrets_pass]
    if username in users and password in passwords:
        logging.info('auth success')
        return username
    logging.info('auth fail')
    return abort(403)

@app.route('/')
@auth.login_required
def latest_xml():
    try:
        request_json = request.get_json()#silent=True)
        storage_client = storage.Client(project=PROJECT_ID)
        bucket = storage_client.get_bucket(GCS_BUCKET)
        blob = bucket.get_blob('latest_pull.xml')
        latest_xml = blob.download_as_string()
        logging.info('Loaded blob from GCS')
        return(latest_xml)
    except exception as e:
        logging.error(str(e))
        logging.error("Failed to load blob from GCS")
        sys.exit(1)

if __name__ == '__main__':
    app.run()

I've tried setting the entrypoint as both the main function as well as the auth function to no avail. My question is: is it possible to even use basic auth in a GCP Cloud Function or am I barking up the wrong tree here?

1 Answers1

3

Your function doesn't enforce the standard signature for http function

def latest_xml(request):
  ...

Here you use a flask web server, which is not need, and not used by Cloud Functions. However, I recommend you to have a look to Cloud Run, and to add a simple and generic Dockerfile to deploy . You can deploy your "function" as-is in a container and to have the same behavior as Cloud Functions.

EDIT

When you use flask, the request object is global for each request. You use it like this:

       request_json = request.get_json()#silent=True)

With Cloud Functions, this object is caught by the Cloud Functions platform and passed in parameter to your function.

In the request object, you have the body of the request, useless in GET for example. But also, all the request context: headers, user agent, source ip,...

guillaume blaquiere
  • 66,369
  • 2
  • 47
  • 76
  • Thank you for your response! I had looked into Cloud Run briefly and was considering App Engine as the alternative, but upon further review with your direction I agree that CR is a better fit. I read through the first link you shared with me as well and had a question on the matter of passing the request as the parameter of the main function. In cases where a single consumer will access the endpoint and there is no body in the request, what does requiring the arg do? In [the docs example](https://flask-httpauth.readthedocs.io/en/latest/) there is similarly no arg, but is that case different? – Arya Eshraghi Jul 22 '20 at 11:31