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

Commit 4bbcc10

Browse files
authored
feat!: Add ability to store breakpoint data in firebase realtime database (#1076)
Setting the "useFirebase" config value will now use a user's Firebase Realtime Database to store breakpoints. The feature is fully functional but expect additional changes in order to improve error handling and documentation. This change will have no functional impact unless the useFirebase config option is set to true.
1 parent 5636370 commit 4bbcc10

File tree

11 files changed

+1181
-393
lines changed

11 files changed

+1181
-393
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
"console-log-level": "^1.4.0",
5151
"extend": "^3.0.2",
5252
"findit2": "^2.2.3",
53+
"firebase-admin": "^9.11.1",
5354
"gcp-metadata": "^4.0.0",
5455
"p-limit": "^3.0.1",
5556
"semver": "^7.0.0",

src/agent/config.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -362,6 +362,21 @@ export interface ResolvedDebugAgentConfig extends GoogleAuthOptions {
362362
* in defaultConfig.
363363
*/
364364
resetV8DebuggerThreshold: number;
365+
366+
/**
367+
* If set, use Firebase Realtime Database as the backend instead of the
368+
* Cloud Debugger API. Requires many things, which will be documented later.
369+
*/
370+
useFirebase: boolean;
371+
372+
/**
373+
* If set, use this key for Firebase activities instead of default google credentials.
374+
*/
375+
firebaseKeyPath?: string;
376+
/**
377+
* If set, use this as the firebase database url. If not set, a FIXME default will be used.
378+
*/
379+
firebaseDbUrl?: string;
365380
}
366381

367382
export interface StackdriverConfig extends GoogleAuthOptions {
@@ -412,4 +427,6 @@ export const defaultConfig: ResolvedDebugAgentConfig = {
412427
forceNewAgent_: false,
413428
testMode_: false,
414429
resetV8DebuggerThreshold: 30,
430+
431+
useFirebase: false,
415432
};

src/agent/controller.ts

Lines changed: 17 additions & 146 deletions
Original file line numberDiff line numberDiff line change
@@ -16,46 +16,14 @@
1616
* @module debug/controller
1717
*/
1818

19-
import {ServiceObject} from '@google-cloud/common';
20-
import * as assert from 'assert';
21-
import * as qs from 'querystring';
22-
import * as t from 'teeny-request';
23-
24-
import {URL} from 'url';
25-
26-
import {Debug} from '../client/stackdriver/debug';
2719
import {Debuggee} from '../debuggee';
2820
import * as stackdriver from '../types/stackdriver';
2921

30-
export class Controller extends ServiceObject {
31-
private nextWaitToken: string | null;
32-
private agentId: string | null;
33-
34-
apiUrl: string;
35-
36-
/**
37-
* @constructor
38-
*/
39-
40-
constructor(debug: Debug, config?: {apiUrl?: string}) {
41-
super({parent: debug, baseUrl: '/controller'});
42-
43-
/** @private {string} */
44-
this.nextWaitToken = null;
45-
this.agentId = null;
46-
47-
this.apiUrl = `https://${debug.apiEndpoint}/v2/controller`;
48-
49-
if (config && config.apiUrl) {
50-
this.apiUrl = config.apiUrl + new URL(this.apiUrl).pathname;
51-
}
52-
}
53-
22+
export interface Controller {
5423
/**
5524
* Register to the API (implementation)
5625
*
5726
* @param {!function(?Error,Object=)} callback
58-
* @private
5927
*/
6028
register(
6129
debuggee: Debuggee,
@@ -66,91 +34,7 @@ export class Controller extends ServiceObject {
6634
agentId: string;
6735
}
6836
) => void
69-
): void {
70-
const options = {
71-
uri: this.apiUrl + '/debuggees/register',
72-
method: 'POST',
73-
json: true,
74-
body: {debuggee},
75-
};
76-
this.request(
77-
options,
78-
(err, body: {debuggee: Debuggee; agentId: string}, response) => {
79-
if (err) {
80-
callback(err);
81-
} else if (response!.statusCode !== 200) {
82-
callback(
83-
new Error('unable to register, statusCode ' + response!.statusCode)
84-
);
85-
} else if (!body.debuggee) {
86-
callback(new Error('invalid response body from server'));
87-
} else {
88-
debuggee.id = body.debuggee.id;
89-
this.agentId = body.agentId;
90-
callback(null, body);
91-
}
92-
}
93-
);
94-
}
95-
96-
/**
97-
* Fetch the list of breakpoints from the server. Assumes we have registered.
98-
* @param {!function(?Error,Object=,Object=)} callback accepting (err, response,
99-
* body)
100-
*/
101-
listBreakpoints(
102-
debuggee: Debuggee,
103-
callback: (
104-
err: Error | null,
105-
response?: t.Response,
106-
body?: stackdriver.ListBreakpointsResponse
107-
) => void
108-
): void {
109-
// eslint-disable-next-line @typescript-eslint/no-this-alias
110-
const that = this;
111-
assert(debuggee.id, 'should have a registered debuggee');
112-
const query: stackdriver.ListBreakpointsQuery = {successOnTimeout: true};
113-
if (that.nextWaitToken) {
114-
query.waitToken = that.nextWaitToken;
115-
}
116-
if (that.agentId) {
117-
query.agentId = that.agentId;
118-
}
119-
120-
const uri =
121-
this.apiUrl +
122-
'/debuggees/' +
123-
encodeURIComponent(debuggee.id) +
124-
'/breakpoints?' +
125-
qs.stringify(query as qs.ParsedUrlQueryInput);
126-
that.request(
127-
{uri, json: true},
128-
(err, body: stackdriver.ListBreakpointsResponse, response) => {
129-
if (!response) {
130-
callback(
131-
err || new Error('unknown error - request response missing')
132-
);
133-
return;
134-
} else if (response.statusCode === 404) {
135-
// The v2 API returns 404 (google.rpc.Code.NOT_FOUND) when the agent
136-
// registration expires. We should re-register.
137-
callback(null, response as {} as t.Response);
138-
return;
139-
} else if (response.statusCode !== 200) {
140-
callback(
141-
new Error(
142-
'unable to list breakpoints, status code ' + response.statusCode
143-
)
144-
);
145-
return;
146-
} else {
147-
body = body || {};
148-
that.nextWaitToken = body.nextWaitToken;
149-
callback(null, response as {} as t.Response, body);
150-
}
151-
}
152-
);
153-
}
37+
): void;
15438

15539
/**
15640
* Update the server about breakpoint state
@@ -162,34 +46,21 @@ export class Controller extends ServiceObject {
16246
debuggee: Debuggee,
16347
breakpoint: stackdriver.Breakpoint,
16448
callback: (err?: Error, body?: {}) => void
165-
): void {
166-
assert(debuggee.id, 'should have a registered debuggee');
49+
): void;
16750

168-
breakpoint.action = 'CAPTURE';
169-
breakpoint.isFinalState = true;
170-
const options = {
171-
uri:
172-
this.apiUrl +
173-
'/debuggees/' +
174-
encodeURIComponent(debuggee.id) +
175-
// TODO: Address the case where `breakpoint.id` is `undefined`.
176-
'/breakpoints/' +
177-
encodeURIComponent(breakpoint.id as string),
178-
json: true,
179-
method: 'PUT',
180-
body: {debuggeeId: debuggee.id, breakpoint},
181-
};
51+
/**
52+
* Start listening to breakpoints updates. The callback will be called when
53+
* there is an unrecoverable error or when the set of active breakpoints has changed.
54+
* @param {!Debuggee} debuggee
55+
* @param {!function(?Error,Object=)} callback accepting (err, breakpoints)
56+
*/
57+
subscribeToBreakpoints(
58+
debuggee: Debuggee,
59+
callback: (err: Error | null, breakpoints: stackdriver.Breakpoint[]) => void
60+
): void;
18261

183-
// We need to have a try/catch here because a JSON.stringify will be done
184-
// by request. Some V8 debug mirror objects get a throw when we attempt to
185-
// stringify them. The try-catch keeps it resilient and avoids crashing the
186-
// user's app.
187-
try {
188-
this.request(options, (err, body /*, response */) => {
189-
callback(err!, body);
190-
});
191-
} catch (error) {
192-
callback(error);
193-
}
194-
}
62+
/**
63+
* Stops the Controller. This is for testing purposes only.
64+
*/
65+
stop(): void;
19566
}

0 commit comments

Comments
 (0)