Skip to content

Commit c8dc031

Browse files
authored
Fix Cloud SQL system tests (#19014)
- Add creation of Fine-grained bucket for ACLs - Add patching of environment variables - Add unique postfix to instances names
1 parent d1a2b47 commit c8dc031

File tree

3 files changed

+91
-11
lines changed

3 files changed

+91
-11
lines changed

β€Žairflow/providers/google/cloud/example_dags/example_cloud_sql_query.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,12 @@
4848
GCP_PROJECT_ID = os.environ.get('GCP_PROJECT_ID', 'example-project')
4949
GCP_REGION = os.environ.get('GCP_REGION', 'europe-west1')
5050

51-
GCSQL_POSTGRES_INSTANCE_NAME_QUERY = os.environ.get('GCSQL_POSTGRES_INSTANCE_NAME_QUERY', 'testpostgres')
51+
GCSQL_POSTGRES_INSTANCE_NAME_QUERY = os.environ.get(
52+
'GCSQL_POSTGRES_INSTANCE_NAME_QUERY', 'test-postgres-query'
53+
)
5254
GCSQL_POSTGRES_DATABASE_NAME = os.environ.get('GCSQL_POSTGRES_DATABASE_NAME', 'postgresdb')
5355
GCSQL_POSTGRES_USER = os.environ.get('GCSQL_POSTGRES_USER', 'postgres_user')
54-
GCSQL_POSTGRES_PASSWORD = os.environ.get('GCSQL_POSTGRES_PASSWORD', 'password')
56+
GCSQL_POSTGRES_PASSWORD = os.environ.get('GCSQL_POSTGRES_PASSWORD', 'JoxHlwrPzwch0gz9')
5557
GCSQL_POSTGRES_PUBLIC_IP = os.environ.get('GCSQL_POSTGRES_PUBLIC_IP', '0.0.0.0')
5658
GCSQL_POSTGRES_PUBLIC_PORT = os.environ.get('GCSQL_POSTGRES_PUBLIC_PORT', 5432)
5759
GCSQL_POSTGRES_CLIENT_CERT_FILE = os.environ.get(
@@ -62,10 +64,10 @@
6264
)
6365
GCSQL_POSTGRES_SERVER_CA_FILE = os.environ.get('GCSQL_POSTGRES_SERVER_CA_FILE', ".key/postgres-server-ca.pem")
6466

65-
GCSQL_MYSQL_INSTANCE_NAME_QUERY = os.environ.get('GCSQL_MYSQL_INSTANCE_NAME_QUERY', 'testmysql')
67+
GCSQL_MYSQL_INSTANCE_NAME_QUERY = os.environ.get('GCSQL_MYSQL_INSTANCE_NAME_QUERY', 'test-mysql-query')
6668
GCSQL_MYSQL_DATABASE_NAME = os.environ.get('GCSQL_MYSQL_DATABASE_NAME', 'mysqldb')
6769
GCSQL_MYSQL_USER = os.environ.get('GCSQL_MYSQL_USER', 'mysql_user')
68-
GCSQL_MYSQL_PASSWORD = os.environ.get('GCSQL_MYSQL_PASSWORD', 'password')
70+
GCSQL_MYSQL_PASSWORD = os.environ.get('GCSQL_MYSQL_PASSWORD', 'JoxHlwrPzwch0gz9')
6971
GCSQL_MYSQL_PUBLIC_IP = os.environ.get('GCSQL_MYSQL_PUBLIC_IP', '0.0.0.0')
7072
GCSQL_MYSQL_PUBLIC_PORT = os.environ.get('GCSQL_MYSQL_PUBLIC_PORT', 3306)
7173
GCSQL_MYSQL_CLIENT_CERT_FILE = os.environ.get('GCSQL_MYSQL_CLIENT_CERT_FILE', ".key/mysql-client-cert.pem")

β€Žtests/providers/google/cloud/operators/test_cloud_sql_system.py

Lines changed: 75 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import random
2020
import string
2121
import time
22+
import uuid
2223

2324
import pytest
2425

@@ -34,27 +35,99 @@
3435
from tests.test_utils.gcp_system_helpers import CLOUD_DAG_FOLDER, GoogleSystemTest, provide_gcp_context
3536

3637
GCP_PROJECT_ID = os.environ.get('GCP_PROJECT_ID', 'project-id')
38+
CLOUD_SQL_BUCKET_NAME = os.environ.get('CLOUD_SQL_BUCKET_NAME', 'INVALID BUCKET NAME')
3739

3840
SQL_QUERY_TEST_HELPER = CloudSqlQueryTestHelper()
3941

4042

43+
@pytest.fixture(scope='class')
44+
def env_patch():
45+
"""
46+
A convenient fixture for environment variables patching.
47+
All modifications will be undone after the requesting test class has finished.
48+
"""
49+
from _pytest.monkeypatch import MonkeyPatch
50+
51+
mpatch = MonkeyPatch()
52+
yield mpatch
53+
mpatch.undo()
54+
55+
56+
@pytest.fixture(scope='module')
57+
def default_instances():
58+
"""
59+
Collect list of environment variables default values
60+
"""
61+
mysql_instance_name = os.environ.get('GCSQL_MYSQL_INSTANCE_NAME', 'test-mysql') + '%s'
62+
mysql_instance_name2 = os.environ.get('GCSQL_MYSQL_INSTANCE_NAME2', 'test-mysql2') + '%s'
63+
mysql_instance_name_query = mysql_instance_name + QUERY_SUFFIX
64+
postgres_instance_name = os.environ.get('GCSQL_POSTGRES_INSTANCE_NAME', 'test-postgres') + '%s'
65+
postgres_instance_name2 = os.environ.get('GCSQL_POSTGRES_INSTANCE_NAME2', 'test-postgres2') + '%s'
66+
postgres_instance_name_query = postgres_instance_name + QUERY_SUFFIX
67+
instances = [
68+
('GCSQL_MYSQL_INSTANCE_NAME', mysql_instance_name),
69+
('GCSQL_MYSQL_INSTANCE_NAME2', mysql_instance_name2),
70+
('GCSQL_MYSQL_INSTANCE_NAME_QUERY', mysql_instance_name_query),
71+
('GCSQL_POSTGRES_INSTANCE_NAME', postgres_instance_name),
72+
('GCSQL_POSTGRES_INSTANCE_NAME2', postgres_instance_name2),
73+
('GCSQL_POSTGRES_INSTANCE_NAME_QUERY', postgres_instance_name_query),
74+
]
75+
return instances
76+
77+
78+
@pytest.fixture(scope='class', autouse=True)
79+
def set_unique_postfix(env_patch, default_instances):
80+
"""
81+
Generate a unique postfix and add it to an instance name to avoid 409 HTTP error
82+
in case of the instance name was already used during last week
83+
"""
84+
unique_postfix = f'-{uuid.uuid4().hex[:5]}' # generate 5 digits unique postfix
85+
for instance in default_instances:
86+
env_variable, value = instance
87+
env_patch.setenv(env_variable, value % unique_postfix)
88+
89+
90+
@pytest.fixture
91+
def set_mysql_ip(monkeypatch):
92+
"""Set ip address of MySQL instance to GCSQL_MYSQL_PUBLIC_IP env variable"""
93+
with open(os.environ["GCSQL_MYSQL_PUBLIC_IP_FILE"]) as file:
94+
env, ip_address = file.read().split("=")
95+
monkeypatch.setenv(env, ip_address)
96+
97+
98+
@pytest.fixture
99+
def set_postgres_ip(monkeypatch):
100+
"""Set ip address of Postgres instance to GCSQL_POSTGRES_PUBLIC_IP env variable"""
101+
with open(os.environ["GCSQL_POSTGRES_PUBLIC_IP_FILE"]) as file:
102+
env, ip_address = file.read().split("=")
103+
monkeypatch.setenv(env, ip_address)
104+
105+
41106
@pytest.mark.backend("mysql", "postgres")
42107
@pytest.mark.credential_file(GCP_CLOUDSQL_KEY)
43108
class CloudSqlExampleDagsIntegrationTest(GoogleSystemTest):
109+
@provide_gcp_context(GCP_CLOUDSQL_KEY)
44110
def setUp(self):
45111
super().setUp()
112+
# 1. Create Fine-grained bucket to provide ACLs
113+
self.log.info(f'Creating {CLOUD_SQL_BUCKET_NAME} bucket...')
114+
self.execute_cmd(["gsutil", "mb", "-b", "off", f"gs://{CLOUD_SQL_BUCKET_NAME}"])
46115

47116
@provide_gcp_context(GCP_CLOUDSQL_KEY)
48117
def tearDown(self):
49118
if os.path.exists(TEARDOWN_LOCK_FILE):
50119
self.log.info("Skip deleting instances as they were created manually (helps to iterate on tests)")
51120
else:
52-
# Delete instances just in case the test failed and did not cleanup after itself
121+
# 1. Delete instances just in case the test failed and did not cleanup after itself
53122
SQL_QUERY_TEST_HELPER.delete_instances(instance_suffix="-failover-replica")
54123
SQL_QUERY_TEST_HELPER.delete_instances(instance_suffix="-read-replica")
55124
SQL_QUERY_TEST_HELPER.delete_instances()
56125
SQL_QUERY_TEST_HELPER.delete_instances(instance_suffix="2")
57126
SQL_QUERY_TEST_HELPER.delete_service_account_acls()
127+
128+
# 2. Delete bucket
129+
self.log.info(f'Deleting {CLOUD_SQL_BUCKET_NAME} bucket...')
130+
self.execute_cmd(["gsutil", "rm", "-r", f"gs://{CLOUD_SQL_BUCKET_NAME}"])
58131
super().tearDown()
59132

60133
@provide_gcp_context(GCP_CLOUDSQL_KEY)
@@ -79,7 +152,6 @@ class CloudSqlProxySystemTest(GoogleSystemTest):
79152
@classmethod
80153
@provide_gcp_context(GCP_CLOUDSQL_KEY)
81154
def setUpClass(cls):
82-
SQL_QUERY_TEST_HELPER.set_ip_addresses_in_env()
83155
if os.path.exists(TEARDOWN_LOCK_FILE_QUERY):
84156
print(
85157
"Skip creating and setting up instances as they were created manually "
@@ -176,5 +248,6 @@ def test_start_proxy_with_all_instances_specific_version(self):
176248
assert runner.get_proxy_version() == "1.13"
177249

178250
@provide_gcp_context(GCP_CLOUDSQL_KEY)
251+
@pytest.mark.usefixtures('set_mysql_ip', 'set_postgres_ip')
179252
def test_run_example_dag_cloudsql_query(self):
180253
self.run_dag('example_gcp_sql_query', CLOUD_DAG_FOLDER)

β€Žtests/providers/google/cloud/operators/test_cloud_sql_system_helper.py

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,12 +41,14 @@
4141
)
4242
GCSQL_POSTGRES_PUBLIC_IP_FILE = os.environ.get('GCSQL_POSTGRES_PUBLIC_IP_FILE', ".key/postgres-ip.env")
4343
GCSQL_POSTGRES_USER = os.environ.get('GCSQL_POSTGRES_USER', 'postgres_user')
44+
GCSQL_POSTGRES_PASSWORD = os.environ.get('GCSQL_POSTGRES_PASSWORD', 'JoxHlwrPzwch0gz9')
4445
GCSQL_POSTGRES_DATABASE_NAME = os.environ.get('GCSQL_POSTGRES_DATABASE_NAME', 'postgresdb')
4546
GCSQL_MYSQL_CLIENT_CERT_FILE = os.environ.get('GCSQL_MYSQL_CLIENT_CERT_FILE', ".key/mysql-client-cert.pem")
4647
GCSQL_MYSQL_CLIENT_KEY_FILE = os.environ.get('GCSQL_MYSQL_CLIENT_KEY_FILE', ".key/mysql-client-key.pem")
4748
GCSQL_MYSQL_SERVER_CA_FILE = os.environ.get('GCSQL_MYSQL_SERVER_CA_FILE', ".key/mysql-server-ca.pem")
4849
GCSQL_MYSQL_PUBLIC_IP_FILE = os.environ.get('GCSQL_MYSQL_PUBLIC_IP_FILE', ".key/mysql-ip.env")
4950
GCSQL_MYSQL_USER = os.environ.get('GCSQL_MYSQL_USER', 'mysql_user')
51+
GCSQL_MYSQL_PASSWORD = os.environ.get('GCSQL_MYSQL_PASSWORD', 'JoxHlwrPzwch0gz9')
5052
GCSQL_MYSQL_DATABASE_NAME = os.environ.get('GCSQL_MYSQL_DATABASE_NAME', 'mysqldb')
5153

5254
GCSQL_MYSQL_EXPORT_URI = os.environ.get('GCSQL_MYSQL_EXPORT_URI', 'gs://bucketName/fileName')
@@ -80,13 +82,13 @@ def get_absolute_path(path):
8082
def get_postgres_instance_name(instance_suffix=''):
8183
if instance_suffix is None:
8284
return None
83-
return os.environ.get('GCSQL_POSTGRES_INSTANCE_NAME', 'testpostgres') + instance_suffix
85+
return os.environ.get('GCSQL_POSTGRES_INSTANCE_NAME', 'test-postgres') + instance_suffix
8486

8587

8688
def get_mysql_instance_name(instance_suffix=''):
8789
if instance_suffix is None:
8890
return None
89-
return os.environ.get('GCSQL_MYSQL_INSTANCE_NAME', 'testmysql') + instance_suffix
91+
return os.environ.get('GCSQL_MYSQL_INSTANCE_NAME', 'test-mysql') + instance_suffix
9092

9193

9294
class CloudSqlQueryTestHelper(CommandExecutor):
@@ -216,6 +218,7 @@ def setup_instances(self, instance_suffix=''):
216218
client_cert_file_mysql,
217219
GCSQL_MYSQL_DATABASE_NAME,
218220
GCSQL_MYSQL_USER,
221+
GCSQL_MYSQL_PASSWORD,
219222
)
220223
)
221224
postgres_thread = Thread(
@@ -227,6 +230,7 @@ def setup_instances(self, instance_suffix=''):
227230
client_cert_file_postgres,
228231
GCSQL_POSTGRES_DATABASE_NAME,
229232
GCSQL_POSTGRES_USER,
233+
GCSQL_POSTGRES_PASSWORD,
230234
)
231235
)
232236
mysql_thread.start()
@@ -320,6 +324,7 @@ def __setup_instance_and_certs(
320324
client_cert_file,
321325
db_name,
322326
db_username,
327+
db_userpassword,
323328
):
324329
self.log.info('Setting up a test %s instance "%s"...', db_version, instance_name)
325330
try:
@@ -336,7 +341,7 @@ def __setup_instance_and_certs(
336341
self.__write_to_file(client_cert_file, self.__get_client_cert(instance_name, client_cert_name))
337342
self.__wait_for_operations(instance_name)
338343
self.__wait_for_operations(instance_name)
339-
self.__create_user(instance_name, db_username)
344+
self.__create_user(instance_name, db_username, db_userpassword)
340345
self.__wait_for_operations(instance_name)
341346
self.__delete_db(instance_name, db_name)
342347
self.__create_db(instance_name, db_name)
@@ -414,7 +419,7 @@ def __get_client_cert(self, instance_name: str, client_cert_name: str) -> bytes:
414419
self.log.info('... Done.')
415420
return output
416421

417-
def __create_user(self, instance_name: str, username: str) -> None:
422+
def __create_user(self, instance_name: str, username: str, userpassword: str) -> None:
418423
self.log.info('Creating user "%s" in Cloud SQL instance "%s"...', username, instance_name)
419424
self.execute_cmd(
420425
[
@@ -428,7 +433,7 @@ def __create_user(self, instance_name: str, username: str) -> None:
428433
'--host',
429434
'%',
430435
'--password',
431-
'JoxHlwrPzwch0gz9',
436+
userpassword,
432437
'--quiet',
433438
]
434439
)

0 commit comments

Comments
 (0)