Skip to content
This repository was archived by the owner on Jan 23, 2024. It is now read-only.

Commit e0c48db

Browse files
authored
feat: Support default-rtdb instance. (#78)
Fixes #77
1 parent c4b54d4 commit e0c48db

2 files changed

Lines changed: 232 additions & 75 deletions

File tree

src/googleclouddebugger/firebase_client.py

Lines changed: 76 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -106,8 +106,10 @@ def __init__(self):
106106
self._mark_active_interval_sec = 60 * 60 # 1 hour in seconds
107107
self._new_updates = threading.Event()
108108
self._breakpoint_subscription = None
109+
self._firebase_app = None
109110

110111
# Events for unit testing.
112+
self.connection_complete = threading.Event()
111113
self.registration_complete = threading.Event()
112114
self.subscription_complete = threading.Event()
113115

@@ -116,6 +118,7 @@ def __init__(self):
116118
#
117119

118120
# Delay before retrying failed request.
121+
self.connect_backoff = backoff.Backoff() # Connect to the DB.
119122
self.register_backoff = backoff.Backoff() # Register debuggee.
120123
self.subscribe_backoff = backoff.Backoff() # Subscribe to updates.
121124
self.update_backoff = backoff.Backoff() # Update breakpoint.
@@ -193,7 +196,10 @@ def SetupAuth(self,
193196
service_account_json_file: JSON file to use for credentials. If not
194197
provided, will default to application default credentials.
195198
database_url: Firebase realtime database URL to be used. If not
196-
provided, will default to https://{project_id}-cdbg.firebaseio.com
199+
provided, connect attempts to the following DBs will be made, in
200+
order:
201+
https://{project_id}-cdbg.firebaseio.com
202+
https://{project_id}-default-rtdb.firebaseio.com
197203
Raises:
198204
NoProjectIdError: If the project id cannot be determined.
199205
"""
@@ -220,11 +226,7 @@ def SetupAuth(self,
220226
'Please specify the project id using the --project_id flag.')
221227

222228
self._project_id = project_id
223-
224-
if database_url:
225-
self._database_url = database_url
226-
else:
227-
self._database_url = f'https://{self._project_id}-cdbg.firebaseio.com'
229+
self._database_url = database_url
228230

229231
def Start(self):
230232
"""Starts the worker thread."""
@@ -287,15 +289,11 @@ def _MainThreadProc(self):
287289
which will run in its own thread. That thread will be owned by
288290
self._breakpoint_subscription.
289291
"""
290-
# Note: if self._credentials is None, default app credentials will be used.
291-
try:
292-
firebase_admin.initialize_app(self._credentials,
293-
{'databaseURL': self._database_url})
294-
except ValueError:
295-
native.LogWarning(
296-
f'Failed to initialize firebase: {traceback.format_exc()}')
297-
native.LogError('Failed to start debugger agent. Giving up.')
298-
return
292+
connection_required, delay = True, 0
293+
while connection_required:
294+
time.sleep(delay)
295+
connection_required, delay = self._ConnectToDb()
296+
self.connection_complete.set()
299297

300298
registration_required, delay = True, 0
301299
while registration_required:
@@ -343,6 +341,45 @@ def _StartMarkActiveTimer(self):
343341
self._MarkActiveTimerFunc)
344342
self._mark_active_timer.start()
345343

344+
def _ConnectToDb(self):
345+
urls = [self._database_url] if self._database_url is not None else \
346+
[f'https://{self._project_id}-cdbg.firebaseio.com',
347+
f'https://{self._project_id}-default-rtdb.firebaseio.com']
348+
349+
for url in urls:
350+
native.LogInfo(f'Attempting to connect to DB with url: {url}')
351+
352+
status, firebase_app = self._TryInitializeDbForUrl(url)
353+
if status:
354+
native.LogInfo(f'Successfully connected to DB with url: {url}')
355+
self._database_url = url
356+
self._firebase_app = firebase_app
357+
self.connect_backoff.Succeeded()
358+
return (False, 0) # Proceed immediately to registering the debuggee.
359+
360+
return (True, self.connect_backoff.Failed())
361+
362+
def _TryInitializeDbForUrl(self, database_url):
363+
# Note: if self._credentials is None, default app credentials will be used.
364+
app = None
365+
try:
366+
app = firebase_admin.initialize_app(
367+
self._credentials, {'databaseURL': database_url}, name='cdbg')
368+
369+
if self._CheckSchemaVersionPresence(app):
370+
return True, app
371+
372+
except ValueError:
373+
native.LogWarning(
374+
f'Failed to initialize firebase: {traceback.format_exc()}')
375+
376+
# This is the failure path, if we hit here we must cleanup the app handle
377+
if app is not None:
378+
firebase_admin.delete_app(app)
379+
app = None
380+
381+
return False, app
382+
346383
def _RegisterDebuggee(self):
347384
"""Single attempt to register the debuggee.
348385
@@ -371,11 +408,12 @@ def _RegisterDebuggee(self):
371408
else:
372409
debuggee_path = f'cdbg/debuggees/{self._debuggee_id}'
373410
native.LogInfo(
374-
f'registering at {self._database_url}, path: {debuggee_path}')
411+
f'Registering at {self._database_url}, path: {debuggee_path}')
375412
debuggee_data = copy.deepcopy(debuggee)
376413
debuggee_data['registrationTimeUnixMsec'] = {'.sv': 'timestamp'}
377414
debuggee_data['lastUpdateTimeUnixMsec'] = {'.sv': 'timestamp'}
378-
firebase_admin.db.reference(debuggee_path).set(debuggee_data)
415+
firebase_admin.db.reference(debuggee_path,
416+
self._firebase_app).set(debuggee_data)
379417

380418
native.LogInfo(
381419
f'Debuggee registered successfully, ID: {self._debuggee_id}')
@@ -388,10 +426,21 @@ def _RegisterDebuggee(self):
388426
native.LogInfo(f'Failed to register debuggee: {repr(e)}')
389427
return (True, self.register_backoff.Failed())
390428

429+
def _CheckSchemaVersionPresence(self, firebase_app):
430+
path = f'cdbg/schema_version'
431+
try:
432+
snapshot = firebase_admin.db.reference(path, firebase_app).get()
433+
# The value doesn't matter; just return true if there's any value.
434+
return snapshot is not None
435+
except BaseException as e:
436+
native.LogInfo(
437+
f'Failed to check schema version presence at {path}: {repr(e)}')
438+
return False
439+
391440
def _CheckDebuggeePresence(self):
392441
path = f'cdbg/debuggees/{self._debuggee_id}/registrationTimeUnixMsec'
393442
try:
394-
snapshot = firebase_admin.db.reference(path).get()
443+
snapshot = firebase_admin.db.reference(path, self._firebase_app).get()
395444
# The value doesn't matter; just return true if there's any value.
396445
return snapshot is not None
397446
except BaseException as e:
@@ -402,7 +451,8 @@ def _MarkDebuggeeActive(self):
402451
active_path = f'cdbg/debuggees/{self._debuggee_id}/lastUpdateTimeUnixMsec'
403452
try:
404453
server_time = {'.sv': 'timestamp'}
405-
firebase_admin.db.reference(active_path).set(server_time)
454+
firebase_admin.db.reference(active_path,
455+
self._firebase_app).set(server_time)
406456
except BaseException:
407457
native.LogInfo(
408458
f'Failed to mark debuggee active: {traceback.format_exc()}')
@@ -415,7 +465,7 @@ def _SubscribeToBreakpoints(self):
415465

416466
path = f'cdbg/breakpoints/{self._debuggee_id}/active'
417467
native.LogInfo(f'Subscribing to breakpoint updates at {path}')
418-
ref = firebase_admin.db.reference(path)
468+
ref = firebase_admin.db.reference(path, self._firebase_app)
419469
try:
420470
self._breakpoint_subscription = ref.listen(self._ActiveBreakpointCallback)
421471
return (False, 0)
@@ -508,15 +558,17 @@ def _TransmitBreakpointUpdates(self):
508558

509559
# First, remove from the active breakpoints.
510560
bp_ref = firebase_admin.db.reference(
511-
f'cdbg/breakpoints/{self._debuggee_id}/active/{bp_id}')
561+
f'cdbg/breakpoints/{self._debuggee_id}/active/{bp_id}',
562+
self._firebase_app)
512563
bp_ref.delete()
513564

514565
summary_data = breakpoint_data
515566
# Save snapshot data for snapshots only.
516567
if is_snapshot:
517568
# Note that there may not be snapshot data.
518569
bp_ref = firebase_admin.db.reference(
519-
f'cdbg/breakpoints/{self._debuggee_id}/snapshot/{bp_id}')
570+
f'cdbg/breakpoints/{self._debuggee_id}/snapshot/{bp_id}',
571+
self._firebase_app)
520572
bp_ref.set(breakpoint_data)
521573

522574
# Now strip potential snapshot data.
@@ -527,7 +579,8 @@ def _TransmitBreakpointUpdates(self):
527579

528580
# Then add it to the list of final breakpoints.
529581
bp_ref = firebase_admin.db.reference(
530-
f'cdbg/breakpoints/{self._debuggee_id}/final/{bp_id}')
582+
f'cdbg/breakpoints/{self._debuggee_id}/final/{bp_id}',
583+
self._firebase_app)
531584
bp_ref.set(summary_data)
532585

533586
native.LogInfo(f'Breakpoint {bp_id} update transmitted successfully')

0 commit comments

Comments
 (0)