From 722b0fd43e48056e5a77aaa00c06ed697b052850 Mon Sep 17 00:00:00 2001 From: Ace Nassri Date: Fri, 24 Aug 2018 19:23:50 -0700 Subject: [PATCH 1/9] Add GCF Python SQL samples Change-Id: Ib60712a5f3808b1fb99088e5e637304ef21e98c7 --- functions/sql/mysql_sample.py | 45 ++++++++++++++++++++++++++++++++ functions/sql/mysql_test.py | 35 +++++++++++++++++++++++++ functions/sql/postgres_sample.py | 43 ++++++++++++++++++++++++++++++ functions/sql/postgres_test.py | 35 +++++++++++++++++++++++++ functions/sql/requirements.txt | 2 ++ 5 files changed, 160 insertions(+) create mode 100644 functions/sql/mysql_sample.py create mode 100644 functions/sql/mysql_test.py create mode 100644 functions/sql/postgres_sample.py create mode 100644 functions/sql/postgres_test.py create mode 100644 functions/sql/requirements.txt diff --git a/functions/sql/mysql_sample.py b/functions/sql/mysql_sample.py new file mode 100644 index 00000000000..cc56d611dbb --- /dev/null +++ b/functions/sql/mysql_sample.py @@ -0,0 +1,45 @@ +# Copyright 2018 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the 'License'); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an 'AS IS' BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# [START functions_sql_mysql] +from os import getenv + +import pymysql + +is_production = getenv('SUPERVISOR_HOSTNAME') is not None + +# TODO(developer): specify SQL connection details +mysql_config = { + 'user': getenv('SQL_USER'), + 'password': getenv('SQL_PASSWORD'), + 'db': getenv('SQL_DATABASE'), + 'charset': 'utf8mb4', + 'cursorclass': pymysql.cursors.DictCursor, + 'autocommit': True +} + +if is_production: + mysql_config['unix_socket'] = \ + '/cloudsql/' + getenv('INSTANCE_CONNECTION_NAME') + +# Create SQL connection globally to enable reuse +mysql_connection = pymysql.connect(**mysql_config) + + +def mysql_demo(request): + with mysql_connection.cursor() as cursor: + cursor.execute('SELECT NOW() as now') + results = cursor.fetchone() + return str(results['now']) +# [END functions_sql_mysql] diff --git a/functions/sql/mysql_test.py b/functions/sql/mysql_test.py new file mode 100644 index 00000000000..61ddd138c7d --- /dev/null +++ b/functions/sql/mysql_test.py @@ -0,0 +1,35 @@ +# Copyright 2018 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the 'License'); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an 'AS IS' BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os +import re + +import mock + +env_vars = { + 'SQL_USER': os.getenv('MYSQL_USER'), + 'SQL_PASSWORD': os.getenv('MYSQL_PASSWORD'), + 'SQL_NAME': os.getenv('MYSQL_NAME'), + 'INSTANCE_CONNECTION_NAME': + os.getenv('INSTANCE_CONNECTION_PREFIX') + '-mysql' +} + +date_regex = re.compile('\d{4}-\d{1,2}-\d{1,2}\s\d{1,2}:\d{1,2}:\d{1,2}') + + +@mock.patch.dict(os.environ, env_vars) +def test_mysql(): + import mysql_sample + results = mysql_sample.mysql_demo(None) + assert date_regex.match(results) diff --git a/functions/sql/postgres_sample.py b/functions/sql/postgres_sample.py new file mode 100644 index 00000000000..91688eb5457 --- /dev/null +++ b/functions/sql/postgres_sample.py @@ -0,0 +1,43 @@ +# Copyright 2018 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the 'License'); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an 'AS IS' BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# [START functions_sql_postgres] +from os import getenv + +import psycopg2 + +is_production = getenv('SUPERVISOR_HOSTNAME') is not None + +# TODO(developer): specify SQL connection details +pg_config = { + 'user': getenv('SQL_USER'), + 'password': getenv('SQL_PASSWORD'), + 'dbname': getenv('SQL_DATABASE'), +} + +if is_production: + pg_config['host'] = '/cloudsql/' + getenv('INSTANCE_CONNECTION_NAME') +else: + pg_config['host'] = 'localhost' + +# Create SQL connection globally to enable reuse +pg_connection = psycopg2.connect(**pg_config) + + +def postgres_demo(request): + with pg_connection.cursor() as cursor: + cursor.execute('SELECT NOW() as now') + results = cursor.fetchone() + return str(results[0]) +# [END functions_sql_postgres] diff --git a/functions/sql/postgres_test.py b/functions/sql/postgres_test.py new file mode 100644 index 00000000000..81edbc1e1c6 --- /dev/null +++ b/functions/sql/postgres_test.py @@ -0,0 +1,35 @@ +# Copyright 2018 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the 'License'); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an 'AS IS' BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os +import re + +import mock + +env_vars = { + 'SQL_USER': os.getenv('POSTGRES_USER'), + 'SQL_PASSWORD': os.getenv('POSTGRES_PASSWORD'), + 'SQL_NAME': os.getenv('POSTGRES_NAME'), + 'INSTANCE_CONNECTION_NAME': + os.getenv('INSTANCE_CONNECTION_PREFIX') + '-mysql' +} + +date_regex = re.compile('\d{4}-\d{1,2}-\d{1,2}\s\d{1,2}:\d{1,2}:\d{1,2}') + + +@mock.patch.dict(os.environ, env_vars) +def test_postgres(): + import postgres_sample + results = postgres_sample.postgres_demo(None) + assert date_regex.match(results) diff --git a/functions/sql/requirements.txt b/functions/sql/requirements.txt new file mode 100644 index 00000000000..163e6df8358 --- /dev/null +++ b/functions/sql/requirements.txt @@ -0,0 +1,2 @@ +psycopg2==2.7.5 +PyMySQL==0.9.2 From 292eaa0a3fdb42e076a72bd79ac731e007618e13 Mon Sep 17 00:00:00 2001 From: Ace Nassri Date: Mon, 27 Aug 2018 13:11:50 -0700 Subject: [PATCH 2/9] Update test SQL instance name Change-Id: I0c6ca84695a1a593f46ddd922507eb0cb1e56879 --- functions/sql/mysql_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/functions/sql/mysql_test.py b/functions/sql/mysql_test.py index 61ddd138c7d..e6d5fea0fb6 100644 --- a/functions/sql/mysql_test.py +++ b/functions/sql/mysql_test.py @@ -22,7 +22,7 @@ 'SQL_PASSWORD': os.getenv('MYSQL_PASSWORD'), 'SQL_NAME': os.getenv('MYSQL_NAME'), 'INSTANCE_CONNECTION_NAME': - os.getenv('INSTANCE_CONNECTION_PREFIX') + '-mysql' + os.getenv('INSTANCE_CONNECTION_PREFIX') } date_regex = re.compile('\d{4}-\d{1,2}-\d{1,2}\s\d{1,2}:\d{1,2}:\d{1,2}') From df9a6ba940f00610c380e9693b6fb1ad694f2636 Mon Sep 17 00:00:00 2001 From: Ace Nassri Date: Tue, 28 Aug 2018 16:22:54 -0700 Subject: [PATCH 3/9] Add SQL Kokoro test files Change-Id: Ia59f803bb2bd50842f047a6570ebbc86171d2898 --- .kokoro/presubmit_tests_functions_sql.cfg | 15 +++++++++++++++ .kokoro/system_tests.sh | 10 ++++++++++ functions/sql/mysql_test.py | 3 +-- functions/sql/postgres_test.py | 3 +-- testing/secrets.tar.enc | Bin 3088 -> 10272 bytes testing/test-env.tmpl.sh | 12 ++++++++++++ 6 files changed, 39 insertions(+), 4 deletions(-) create mode 100644 .kokoro/presubmit_tests_functions_sql.cfg diff --git a/.kokoro/presubmit_tests_functions_sql.cfg b/.kokoro/presubmit_tests_functions_sql.cfg new file mode 100644 index 00000000000..a4fb2912164 --- /dev/null +++ b/.kokoro/presubmit_tests_functions_sql.cfg @@ -0,0 +1,15 @@ +# Format: //devtools/kokoro/config/proto/build.proto + +# Download secrets from Cloud Storage. +gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/python-docs-samples" + +# Tell the trampoline which build file to use. +env_vars: { + key: "TRAMPOLINE_BUILD_FILE" + value: "github/python-docs-samples/.kokoro/system_tests.sh" +} + +env_vars: { + key: "NOX_SESSION" + value: "functions and sql and py36 and not venv" +} diff --git a/.kokoro/system_tests.sh b/.kokoro/system_tests.sh index e615b69b8b9..80b9a3173e5 100755 --- a/.kokoro/system_tests.sh +++ b/.kokoro/system_tests.sh @@ -26,9 +26,19 @@ source ./testing/test-env.sh export GOOGLE_APPLICATION_CREDENTIALS=$(pwd)/testing/service-account.json export GOOGLE_CLIENT_SECRETS=$(pwd)/testing/client-secrets.json +# Run Cloud SQL proxy, if required +if [ -n "${CLOUD_SQL_PROXY}" ]; then + cloud_sql_proxy -instances="${MYSQL_INSTANCE}"=tcp:3306 & + cloud_sql_proxy -instances="${POSTGRES_INSTANCE}"=tcp:5432 & +fi + # Run tests nox -k "${NOX_SESSION}" || ret_code=$? +if [ -n "${CLOUD_SQL_PROXY}" ]; then + killall cloud_sql_proxy || true +fi + # Workaround for Kokoro permissions issue: delete secrets rm testing/{test-env.sh,client-secrets.json,service-account.json} diff --git a/functions/sql/mysql_test.py b/functions/sql/mysql_test.py index e6d5fea0fb6..1d0dd0d6d4e 100644 --- a/functions/sql/mysql_test.py +++ b/functions/sql/mysql_test.py @@ -21,8 +21,7 @@ 'SQL_USER': os.getenv('MYSQL_USER'), 'SQL_PASSWORD': os.getenv('MYSQL_PASSWORD'), 'SQL_NAME': os.getenv('MYSQL_NAME'), - 'INSTANCE_CONNECTION_NAME': - os.getenv('INSTANCE_CONNECTION_PREFIX') + 'INSTANCE_CONNECTION_NAME': os.getenv('MYSQL_INSTANCE') } date_regex = re.compile('\d{4}-\d{1,2}-\d{1,2}\s\d{1,2}:\d{1,2}:\d{1,2}') diff --git a/functions/sql/postgres_test.py b/functions/sql/postgres_test.py index 81edbc1e1c6..88dc7da1b98 100644 --- a/functions/sql/postgres_test.py +++ b/functions/sql/postgres_test.py @@ -21,8 +21,7 @@ 'SQL_USER': os.getenv('POSTGRES_USER'), 'SQL_PASSWORD': os.getenv('POSTGRES_PASSWORD'), 'SQL_NAME': os.getenv('POSTGRES_NAME'), - 'INSTANCE_CONNECTION_NAME': - os.getenv('INSTANCE_CONNECTION_PREFIX') + '-mysql' + 'INSTANCE_CONNECTION_NAME': os.getenv('POSTGRES_INSTANCE') } date_regex = re.compile('\d{4}-\d{1,2}-\d{1,2}\s\d{1,2}:\d{1,2}:\d{1,2}') diff --git a/testing/secrets.tar.enc b/testing/secrets.tar.enc index f57ce1afea8aa061d8073329729c3c726420650a..54025165b910ba049ce868f0b583566537ef010e 100644 GIT binary patch literal 10272 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 Date: Fri, 7 Sep 2018 10:50:32 -0700 Subject: [PATCH 4/9] Address comments Change-Id: I0bad3298fa138430a4073ce108412fd4a6af5854 --- functions/sql/mysql_test.py | 5 +---- functions/sql/postgres_test.py | 5 +---- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/functions/sql/mysql_test.py b/functions/sql/mysql_test.py index 1d0dd0d6d4e..4612630ffe0 100644 --- a/functions/sql/mysql_test.py +++ b/functions/sql/mysql_test.py @@ -24,11 +24,8 @@ 'INSTANCE_CONNECTION_NAME': os.getenv('MYSQL_INSTANCE') } -date_regex = re.compile('\d{4}-\d{1,2}-\d{1,2}\s\d{1,2}:\d{1,2}:\d{1,2}') - @mock.patch.dict(os.environ, env_vars) def test_mysql(): import mysql_sample - results = mysql_sample.mysql_demo(None) - assert date_regex.match(results) + mysql_sample.mysql_demo(None) diff --git a/functions/sql/postgres_test.py b/functions/sql/postgres_test.py index 88dc7da1b98..8a20ef5d6af 100644 --- a/functions/sql/postgres_test.py +++ b/functions/sql/postgres_test.py @@ -24,11 +24,8 @@ 'INSTANCE_CONNECTION_NAME': os.getenv('POSTGRES_INSTANCE') } -date_regex = re.compile('\d{4}-\d{1,2}-\d{1,2}\s\d{1,2}:\d{1,2}:\d{1,2}') - @mock.patch.dict(os.environ, env_vars) def test_postgres(): import postgres_sample - results = postgres_sample.postgres_demo(None) - assert date_regex.match(results) + postgres_sample.postgres_demo(None) From f426dda61bbaeff509b79b90d8f0eb560df10068 Mon Sep 17 00:00:00 2001 From: Ace Nassri Date: Fri, 7 Sep 2018 17:26:24 -0700 Subject: [PATCH 5/9] Switch python samples to pools Change-Id: Ibf5a90b3b8c02596f441b7d77cdf11b416260437 --- functions/sql/postgres_sample.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/functions/sql/postgres_sample.py b/functions/sql/postgres_sample.py index 91688eb5457..10a7809602f 100644 --- a/functions/sql/postgres_sample.py +++ b/functions/sql/postgres_sample.py @@ -15,7 +15,7 @@ # [START functions_sql_postgres] from os import getenv -import psycopg2 +from psycopg2.pool import SimpleConnectionPool is_production = getenv('SUPERVISOR_HOSTNAME') is not None @@ -31,13 +31,14 @@ else: pg_config['host'] = 'localhost' -# Create SQL connection globally to enable reuse -pg_connection = psycopg2.connect(**pg_config) +pg_pool = SimpleConnectionPool(1, 1, **pg_config) def postgres_demo(request): - with pg_connection.cursor() as cursor: + with pg_pool.getconn().cursor() as cursor: cursor.execute('SELECT NOW() as now') results = cursor.fetchone() return str(results[0]) # [END functions_sql_postgres] + +print(postgres_demo(None)) \ No newline at end of file From 8a6e7f8c76a557a2a9090b332edffba6b230f462 Mon Sep 17 00:00:00 2001 From: Ace Nassri Date: Mon, 10 Sep 2018 10:49:37 -0700 Subject: [PATCH 6/9] Use connections for Python MySQL, as libraries don't support pooling Change-Id: I3efeabd192be9c2c987846b0df7dcced016ddc49 --- functions/sql/mysql_sample.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/functions/sql/mysql_sample.py b/functions/sql/mysql_sample.py index cc56d611dbb..1856b35b5ab 100644 --- a/functions/sql/mysql_sample.py +++ b/functions/sql/mysql_sample.py @@ -37,8 +37,20 @@ mysql_connection = pymysql.connect(**mysql_config) +def __get_cursor(): + """ + Helper function to get a cursor + PyMySQL does NOT automatically reconnect, + so we must reconnect explicitly using ping() + """ + try: + return mysql_connection.cursor() + except: + mysql_connection.ping(reconnect=True) + return mysql_connection.cursor() + def mysql_demo(request): - with mysql_connection.cursor() as cursor: + with __get_cursor() as cursor: cursor.execute('SELECT NOW() as now') results = cursor.fetchone() return str(results['now']) From be10b39b3a7cbe796c95003cf0e325663bfd59de Mon Sep 17 00:00:00 2001 From: Ace Nassri Date: Mon, 10 Sep 2018 10:49:51 -0700 Subject: [PATCH 7/9] Fix lint + tests Change-Id: I9d2901a737ab0bb7d74edefa69876704846fdf10 --- functions/sql/mysql_sample.py | 9 +++++---- functions/sql/mysql_test.py | 14 +------------- functions/sql/postgres_sample.py | 2 -- functions/sql/postgres_test.py | 14 +------------- 4 files changed, 7 insertions(+), 32 deletions(-) diff --git a/functions/sql/mysql_sample.py b/functions/sql/mysql_sample.py index 1856b35b5ab..f13a6495930 100644 --- a/functions/sql/mysql_sample.py +++ b/functions/sql/mysql_sample.py @@ -21,9 +21,9 @@ # TODO(developer): specify SQL connection details mysql_config = { - 'user': getenv('SQL_USER'), - 'password': getenv('SQL_PASSWORD'), - 'db': getenv('SQL_DATABASE'), + 'user': getenv('MYSQL_USER'), + 'password': getenv('MYSQL_PASSWORD'), + 'db': getenv('MYSQL_DATABASE'), 'charset': 'utf8mb4', 'cursorclass': pymysql.cursors.DictCursor, 'autocommit': True @@ -45,10 +45,11 @@ def __get_cursor(): """ try: return mysql_connection.cursor() - except: + except Exception: mysql_connection.ping(reconnect=True) return mysql_connection.cursor() + def mysql_demo(request): with __get_cursor() as cursor: cursor.execute('SELECT NOW() as now') diff --git a/functions/sql/mysql_test.py b/functions/sql/mysql_test.py index 4612630ffe0..240d74a89e0 100644 --- a/functions/sql/mysql_test.py +++ b/functions/sql/mysql_test.py @@ -12,20 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. -import os -import re +import mysql_sample -import mock -env_vars = { - 'SQL_USER': os.getenv('MYSQL_USER'), - 'SQL_PASSWORD': os.getenv('MYSQL_PASSWORD'), - 'SQL_NAME': os.getenv('MYSQL_NAME'), - 'INSTANCE_CONNECTION_NAME': os.getenv('MYSQL_INSTANCE') -} - - -@mock.patch.dict(os.environ, env_vars) def test_mysql(): - import mysql_sample mysql_sample.mysql_demo(None) diff --git a/functions/sql/postgres_sample.py b/functions/sql/postgres_sample.py index 10a7809602f..e92d92dab1b 100644 --- a/functions/sql/postgres_sample.py +++ b/functions/sql/postgres_sample.py @@ -40,5 +40,3 @@ def postgres_demo(request): results = cursor.fetchone() return str(results[0]) # [END functions_sql_postgres] - -print(postgres_demo(None)) \ No newline at end of file diff --git a/functions/sql/postgres_test.py b/functions/sql/postgres_test.py index 8a20ef5d6af..8eb5cc43c9e 100644 --- a/functions/sql/postgres_test.py +++ b/functions/sql/postgres_test.py @@ -12,20 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. -import os -import re +import postgres_sample -import mock -env_vars = { - 'SQL_USER': os.getenv('POSTGRES_USER'), - 'SQL_PASSWORD': os.getenv('POSTGRES_PASSWORD'), - 'SQL_NAME': os.getenv('POSTGRES_NAME'), - 'INSTANCE_CONNECTION_NAME': os.getenv('POSTGRES_INSTANCE') -} - - -@mock.patch.dict(os.environ, env_vars) def test_postgres(): - import postgres_sample postgres_sample.postgres_demo(None) From 686ee00a58d140dae63e428df7a1f906b9cfaaf5 Mon Sep 17 00:00:00 2001 From: Ace Nassri Date: Mon, 10 Sep 2018 11:29:06 -0700 Subject: [PATCH 8/9] Fix nits Change-Id: I0bbcf8e32e6fd2f8fa8ef942d3b089418f01f502 --- functions/sql/mysql_sample.py | 11 +++++++---- functions/sql/postgres_sample.py | 14 +++++++++++++- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/functions/sql/mysql_sample.py b/functions/sql/mysql_sample.py index f13a6495930..5f8c6d5e45f 100644 --- a/functions/sql/mysql_sample.py +++ b/functions/sql/mysql_sample.py @@ -34,7 +34,8 @@ '/cloudsql/' + getenv('INSTANCE_CONNECTION_NAME') # Create SQL connection globally to enable reuse -mysql_connection = pymysql.connect(**mysql_config) +# PyMySQL does not include support for connection pooling +mysql_conn = pymysql.connect(**mysql_config) def __get_cursor(): @@ -44,13 +45,15 @@ def __get_cursor(): so we must reconnect explicitly using ping() """ try: - return mysql_connection.cursor() + return mysql_conn.cursor() except Exception: - mysql_connection.ping(reconnect=True) - return mysql_connection.cursor() + mysql_conn.ping(reconnect=True) + return mysql_conn.cursor() def mysql_demo(request): + # Remember to close SQL resources declared while running this function. + # Keep any declared in global scope (e.g. mysql_conn) for later reuse. with __get_cursor() as cursor: cursor.execute('SELECT NOW() as now') results = cursor.fetchone() diff --git a/functions/sql/postgres_sample.py b/functions/sql/postgres_sample.py index e92d92dab1b..9a71ad11cd3 100644 --- a/functions/sql/postgres_sample.py +++ b/functions/sql/postgres_sample.py @@ -31,10 +31,22 @@ else: pg_config['host'] = 'localhost' -pg_pool = SimpleConnectionPool(1, 1, **pg_config) +# Connection pools reuse connections between invocations, +# and handle dropped or expired connections automatically. +pg_pool = None def postgres_demo(request): + global pg_pool + + # Initialize the pool lazily, in case SQL access isn't needed for this + # GCF instance. Doing so minimizes the number of active SQL connections, + # which helps keep your GCF instances under SQL connection limits. + if not pg_pool: + pg_pool = SimpleConnectionPool(1, 1, **pg_config) + + # Remember to close SQL resources declared while running this function. + # Keep any declared in global scope (e.g. pg_pool) for later reuse. with pg_pool.getconn().cursor() as cursor: cursor.execute('SELECT NOW() as now') results = cursor.fetchone() From 55302507163a36425ef89f7d9e7537edc92c215d Mon Sep 17 00:00:00 2001 From: Ace Nassri Date: Mon, 10 Sep 2018 11:53:04 -0700 Subject: [PATCH 9/9] Make Python config consistent with Node samples Change-Id: Ibb0341f51861ec5adcba136903c1df0567e97398 --- functions/sql/mysql_sample.py | 15 +++++++++++---- functions/sql/postgres_sample.py | 15 +++++++++++---- 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/functions/sql/mysql_sample.py b/functions/sql/mysql_sample.py index 5f8c6d5e45f..635fa7309bb 100644 --- a/functions/sql/mysql_sample.py +++ b/functions/sql/mysql_sample.py @@ -20,10 +20,17 @@ is_production = getenv('SUPERVISOR_HOSTNAME') is not None # TODO(developer): specify SQL connection details +CONNECTION_NAME = getenv( + 'INSTANCE_CONNECTION_NAME', + '') +DB_USER = getenv('MYSQL_USER', '') +DB_PASSWORD = getenv('MYSQL_PASSWORD', '') +DB_NAME = getenv('MYSQL_DATABASE', '') + mysql_config = { - 'user': getenv('MYSQL_USER'), - 'password': getenv('MYSQL_PASSWORD'), - 'db': getenv('MYSQL_DATABASE'), + 'user': DB_USER, + 'password': DB_PASSWORD, + 'db': DB_NAME, 'charset': 'utf8mb4', 'cursorclass': pymysql.cursors.DictCursor, 'autocommit': True @@ -31,7 +38,7 @@ if is_production: mysql_config['unix_socket'] = \ - '/cloudsql/' + getenv('INSTANCE_CONNECTION_NAME') + '/cloudsql/' + CONNECTION_NAME # Create SQL connection globally to enable reuse # PyMySQL does not include support for connection pooling diff --git a/functions/sql/postgres_sample.py b/functions/sql/postgres_sample.py index 9a71ad11cd3..57b830f5a9e 100644 --- a/functions/sql/postgres_sample.py +++ b/functions/sql/postgres_sample.py @@ -20,14 +20,21 @@ is_production = getenv('SUPERVISOR_HOSTNAME') is not None # TODO(developer): specify SQL connection details +CONNECTION_NAME = getenv( + 'INSTANCE_CONNECTION_NAME', + '') +DB_USER = getenv('POSTGRES_USER', '') +DB_PASSWORD = getenv('POSTGRES_PASSWORD', '') +DB_NAME = getenv('POSTGRES_DATABASE', '') + pg_config = { - 'user': getenv('SQL_USER'), - 'password': getenv('SQL_PASSWORD'), - 'dbname': getenv('SQL_DATABASE'), + 'user': DB_USER, + 'password': DB_PASSWORD, + 'dbname': DB_NAME, } if is_production: - pg_config['host'] = '/cloudsql/' + getenv('INSTANCE_CONNECTION_NAME') + pg_config['host'] = '/cloudsql/' + CONNECTION_NAME else: pg_config['host'] = 'localhost'