1
votes

I'm getting an error when trying to deploy my Flask-Python code to Google App Engine. I am using PostgreSQL as my database. This is the error:

Traceback (most recent call last):
  File "/env/lib/python3.6/site-packages/gunicorn/arbiter.py", line 557, in spawn_worker
    worker.init_process()
  File "/env/lib/python3.6/site-packages/gunicorn/workers/base.py", line 126, in init_process
    self.load_wsgi()
  File "/env/lib/python3.6/site-packages/gunicorn/workers/base.py", line 136, in load_wsgi
    self.wsgi = self.app.wsgi()
  File "/env/lib/python3.6/site-packages/gunicorn/app/base.py", line 67, in wsgi
    self.callable = self.load()
  File "/env/lib/python3.6/site-packages/gunicorn/app/wsgiapp.py", line 65, in load
    return self.load_wsgiapp()
  File "/env/lib/python3.6/site-packages/gunicorn/app/wsgiapp.py", line 52, in load_wsgiapp
    return util.import_app(self.app_uri)
  File "/env/lib/python3.6/site-packages/gunicorn/util.py", line 357, in import_app
    __import__(module)
  File "/home/vmagent/app/main.py", line 20, in <module>
    SQLAlchemy.engine.url.URL(
AttributeError: 'property' object has no attribute 'url'
[2021-01-03 14:35:46 +0000] [8] [INFO] Worker exiting (pid: 8)
[2021-01-03 14:35:46 +0000] [1] [INFO] Shutting down: Master
[2021-01-03 14:35:46 +0000] [1] [INFO] Reason: Worker failed to boot.

main.py code:

from flask import Flask, redirect, url_for, render_template, request, 
from flask_sqlalchemy import SQLAlchemy
import os


# Remember - storing secrets in plaintext is potentially unsafe. Consider using
# something like https://cloud.google.com/secret-manager/docs/overview to help keep
# secrets secret.
db_user = os.environ["DB_USER"]
db_pass = os.environ["DB_PASS"]
db_name = os.environ["DB_NAME"]
db_socket_dir = os.environ.get("DB_SOCKET_DIR", "/cloudsql")
cloud_sql_connection_name = os.environ["CLOUD_SQL_CONNECTION_NAME"]

pool = SQLAlchemy.create_engine(

    # Equivalent URL:
    # postgres+pg8000://<db_user>:<db_pass>@/<db_name>
    #                         ?unix_sock=<socket_path>/<cloud_sql_instance_name>/.s.PGSQL.5432
    SQLAlchemy.engine.url.URL(
        drivername="postgresql+pg8000",
        username=db_user,  # e.g. "my-database-user"
        password=db_pass,  # e.g. "my-database-password"
        database=db_name,  # e.g. "my-database-name"
        query={
            "unix_sock": "{}/{}/.s.PGSQL.5432".format(
                db_socket_dir,  # e.g. "/cloudsql"
                cloud_sql_connection_name)  # i.e "<PROJECT-NAME>:<INSTANCE-REGION>:<INSTANCE-NAME>"
        }
    ),
    **db_config
)

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'postgres+psycopg2://postgres:<password>@<public ip>/flaskapp?host=/cloudsql/<instance name>'
app.debug = True
db = SQLAlchemy(app)

class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(80), unique=True)
    age = db.Column(db.String(120), unique=True)
   

    def __init__(self, name, age):
        self.name = name
        self.age = age
        

    def __repr__(self):
        return '<User %r>' % self.username



@app.route("/")
def home():
    return render_template("home.html")



@app.route('/form')
def form():
    title ="form"
    return render_template("form.html", title=title)




@app.route('/success', methods=["POST"])
def success():
    if request.method == 'POST':
        user = User(request.form['name'],request.form['age'])

        
        try:
            db.session.add(user)
            db.session.commit()
            return render_template("success.html")

        except:
            return render_template('form.html')
    else:
        return redirect('/')




db.create_all()

if __name__ == "__main__":
    app.run(host="127.0.0.1",port=8080, debug=True) 

app.yaml code:

entrypoint: "gunicorn -b:$PORT main:app"
env: flex
runtime: python
runtime_config: 
  python_version: 3

env_variables:
  DB_USER: postgres
  DB_PASS: <password>
  DB_NAME: flaskapp
  DB_SOCKET_DIR: /cloudsql
  CLOUD_SQL_CONNECTION_NAME: <instance name>
beta_settings:
  cloud_sql_instances: <instance name>

requirements.txt code:

click==7.1.2
Flask==1.1.2
Flask-SQLAlchemy==2.4.4
itsdangerous==1.1.0
Jinja2==2.11.2
MarkupSafe==1.1.1
psycopg2==2.8.6
SQLAlchemy==1.3.22
Werkzeug==1.0.1
gunicorn==19.6.0

This is guide that I've used prior to deploying the app: https://cloud.google.com/sql/docs/postgres/connect-app-engine-standard. Based on the guide, I've enabled API, installed cloud SDK, and created an SQL instance for PostgreSQL.

What am I doing wrong? Your help will be much appreciated.

1
Looks like you did a bunch of changes on the connection. For example you are not using db_socket_dir = os.environ.get("DB_SOCKET_DIR", "/cloudsql") and you modified the query of the connection replacing "unix_sock": "{}/{}/.s.PGSQL.5432" with 'unix_sock': '/cloudsql/{}/.s.PGSQL.5432' also you removed the socket dir from the query. Could you please try to use the literal example of the documentation?Chris32
Hi @Chris32, I just did that and I got this error db_user = os.environ["DB_USER"] File "/opt/python3.6/lib/python3.6/os.py", line 669, in __getitem__ raise KeyError(key) from None KeyError: 'DB_USER'MajorLazer
I've added the unix_sock and socket dir to the code.MajorLazer
The error message comes since the code shared by @Chris32 is expecting to read a DB_USER environment variable, while you have CLOUD_SQL_USERNAME defined on your app.yaml file. Change it in either the main.py code relevant entry or on the app.yaml file. But you'll need to make all the environment variable names (related to db username, db password, db name and connection string) to have an unified criteria and coherence on both your app.yaml file and application code.Daniel Ocando
I also noticed that your post includes the connection string of your Cloud SQL instance (which also involves Project ID) in several places. I strongly recommend you to redact this information and put a placeholder instead, since this could be considered as sensitive information and it is not best practice to share this in public forums.Daniel Ocando

1 Answers

1
votes

I checked your question and found that the tutorial is about connecting to Cloud SQL from App Engine Standard. However your app.yaml file has env: flex. Therefore you should decide from which product you would like to connect to Cloud SQL.

I wrote a tutorial : "CONNECTING FROM APP ENGINE (FLEX AND STANDARD) TO CLOUD SQL USING TCP AND UNIX DOMAIN SOCKETS 2020"

In case you are trying to connect from App Engine Flex, please do not forget to include in your app.yaml:

Enabling a Unix domain socket

beta_settings:
  cloud_sql_instances: INSTANCE_CONNECTION_NAME

Connecting from App Engine flexible environment to Cloud SQL