Skip to content

Commit 6579506

Browse files
feat: improving logging (#38)
* Add logging to service * Add log_level to readme
1 parent 42da6db commit 6579506

File tree

6 files changed

+79
-26
lines changed

6 files changed

+79
-26
lines changed

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,9 @@ Where:
203203
- **sql_instances**: List of all Cloud SQL instances to configure.
204204
- **private_ip** (optional): Boolean flag for private or public IP addresses.
205205

206-
**Note:** These are placeholder values and should be replaced with proper IAM groups and Cloud SQL instance connection names.
206+
**Note:** These are placeholder values and should be replaced with proper IAM groups and Cloud SQL instance connection names.
207+
208+
There is an additional optional parameter `"log_level"` for the JSON payload which can be set to one of `"INFO"`, `"DEBUG"`, `"WARNING"`, or `"ERROR"` to change severity of outputted logs. Defaults to `"INFO"` when not specified.
207209

208210
It is recommended to save your JSON payload as a `.json` file (ex. "config.json").
209211

app.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
from quart import Quart, request
1717
from google.auth import default
1818
from google.cloud.sql.connector.instance_connection_manager import IPTypes
19+
import logging
20+
import google.cloud.logging
1921
from iam_groups_authn.sync import (
2022
get_credentials,
2123
get_users_with_roles,
@@ -35,6 +37,16 @@
3537

3638
app = Quart(__name__)
3739

40+
# start logging client
41+
client = google.cloud.logging.Client()
42+
client.setup_logging()
43+
log_levels = {
44+
"INFO": logging.INFO,
45+
"DEBUG": logging.DEBUG,
46+
"WARNING": logging.WARNING,
47+
"ERROR": logging.ERROR,
48+
}
49+
3850

3951
@app.route("/", methods=["GET"])
4052
def health_check():
@@ -67,6 +79,15 @@ async def run_groups_authn():
6779
400,
6880
)
6981

82+
# optional param to change log level
83+
log_level = body.get("log_level")
84+
if (
85+
log_level is not None
86+
and type(log_level) is str
87+
and log_level.upper() in log_levels
88+
):
89+
logging.getLogger().setLevel(log_levels[log_level.upper()])
90+
7091
# set ip_type to proper type for connector
7192
ip_type = IPTypes.PRIVATE if private_ip else IPTypes.PUBLIC
7293

@@ -123,6 +144,13 @@ async def run_groups_authn():
123144
if issubclass(type(result), Exception):
124145
raise result
125146

147+
# log IAM users added as database users
148+
added_users = results[0]
149+
logging.debug(
150+
"[%s][%s] Users added to database: %s."
151+
% (instance, group, list(added_users))
152+
)
153+
126154
# revoke group role from users no longer in IAM group
127155
revoke_role_task = asyncio.create_task(
128156
revoke_iam_group_role(
@@ -150,4 +178,17 @@ async def run_groups_authn():
150178
if issubclass(type(result), Exception):
151179
raise result
152180

181+
# log sync info
182+
revoked_users, granted_users = results
183+
logging.info(
184+
"[%s][%s] Sync successful: %s users were revoked group role, %s users were granted group role."
185+
% (instance, group, len(revoked_users), len(granted_users))
186+
)
187+
logging.debug(
188+
"[%s][%s] Users revoked role: %s." % (instance, group, revoked_users)
189+
)
190+
logging.debug(
191+
"[%s][%s] Users granted role: %s." % (instance, group, granted_users)
192+
)
193+
153194
return "Sync successful.", 200

iam_groups_authn/mysql.py

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -68,20 +68,19 @@ def fetch_role_grants(self, group_name):
6868
return results
6969

7070
@async_wrap
71-
def create_group_role(self, group):
71+
def create_group_role(self, role):
7272
"""Verify or create DB role.
7373
74-
Given a group name, verify existance of DB role or create new DB role matching
75-
name of group to manage DB users.
74+
Given a group role, verify existance of role on DB or create new role
75+
to manage DB users.
7676
7777
Args:
78-
db: Database connection pool instance.
79-
group: Name of group to be verified as role or created as new role.
78+
role: Name of group role to be verified or created as new role.
8079
"""
8180
# create connection to db instance
8281
with self.db.connect() as db_connection:
8382
stmt = sqlalchemy.text("CREATE ROLE IF NOT EXISTS :role")
84-
db_connection.execute(stmt, {"role": group})
83+
db_connection.execute(stmt, {"role": role})
8584

8685
@async_wrap
8786
def grant_group_role(self, role, users):
@@ -90,7 +89,6 @@ def grant_group_role(self, role, users):
9089
Given a DB group role and a list of DB users, grant the DB role to each user.
9190
9291
Args:
93-
db: Database connection pool instance.
9492
role: Name of DB role to grant to users.
9593
users: List of DB users' usernames.
9694
"""
@@ -107,7 +105,6 @@ def revoke_group_role(self, role, users):
107105
Given a DB group role and a list of DB users, revoke the DB role from each user.
108106
109107
Args:
110-
db: Database connection pool instance.
111108
role: Name of DB role to revoke from users.
112109
users: List of DB users' usernames.
113110
"""

iam_groups_authn/sql_admin.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,3 +76,4 @@ async def add_missing_db_users(
7676
user_service.insert_db_user(
7777
user, InstanceConnectionName(*instance_connection_name.split(":"))
7878
)
79+
return missing_db_users

iam_groups_authn/sync.py

Lines changed: 28 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,9 @@ def get_group_members(self, group):
6060
return members
6161
# handle errors if IAM group does not exist etc.
6262
except HttpError as e:
63-
print(f"Could not get IAM group `{group}`. Error: {e}")
64-
raise
63+
raise HttpError(
64+
f"Error: Failed to get IAM members of IAM group `{group}`. Verify group exists and is configured correctly."
65+
) from e
6566

6667
@async_wrap
6768
def get_db_users(self, instance_connection_name):
@@ -80,16 +81,21 @@ def get_db_users(self, instance_connection_name):
8081
"""
8182
# build service to call SQL Admin API
8283
service = build("sqladmin", "v1beta4", credentials=self.creds)
83-
results = (
84-
service.users()
85-
.list(
86-
project=instance_connection_name.project,
87-
instance=instance_connection_name.instance,
84+
try:
85+
results = (
86+
service.users()
87+
.list(
88+
project=instance_connection_name.project,
89+
instance=instance_connection_name.instance,
90+
)
91+
.execute()
8892
)
89-
.execute()
90-
)
91-
users = results.get("items", [])
92-
return users
93+
users = results.get("items", [])
94+
return users
95+
except Exception as e:
96+
raise Exception(
97+
f"Error: Failed to get the database users for instance `{instance_connection_name}`. Verify instance connection name and instance details."
98+
) from e
9399

94100
def insert_db_user(self, user_email, instance_connection_name):
95101
"""Create DB user from IAM user.
@@ -117,10 +123,9 @@ def insert_db_user(self, user_email, instance_connection_name):
117123
)
118124
return
119125
except Exception as e:
120-
print(
121-
f"Could not add IAM user `{user_email}` to DB Instance `{instance_connection_name.instance}`. Error: {e}"
122-
)
123-
raise
126+
raise Exception(
127+
f"Error: Failed to add IAM user `{user_email}` to Cloud SQL database instance `{instance_connection_name.instance}`."
128+
) from e
124129

125130

126131
async def get_users_with_roles(role_service, role):
@@ -173,8 +178,10 @@ def get_credentials(creds, scopes):
173178
# if not valid, refresh credentials
174179
if not updated_credentials.valid:
175180
updated_credentials.refresh(request)
176-
except Exception:
177-
raise
181+
except Exception as e:
182+
raise Exception(
183+
"Error: Failed to get proper credentials for service. Verify service account used to run service."
184+
) from e
178185

179186
return updated_credentials
180187

@@ -227,6 +234,8 @@ async def revoke_iam_group_role(
227234
# revoke group role from users no longer in IAM group
228235
await role_service.revoke_group_role(role, users_to_revoke)
229236

237+
return users_to_revoke
238+
230239

231240
async def grant_iam_group_role(
232241
role_service,
@@ -253,3 +262,5 @@ async def grant_iam_group_role(
253262
username for username in mysql_usernames if username not in users_with_roles
254263
]
255264
await role_service.grant_group_role(role, users_to_grant)
265+
266+
return users_to_grant

requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,4 @@ google-api-python-client==2.19.1
55
google-auth==2.0.0
66
PyMySQL==1.0.2
77
cloud-sql-python-connector[pymysql]==0.4.1
8+
google-cloud-logging==2.6.0

0 commit comments

Comments
 (0)