Skip to content

Commit 67f1a3e

Browse files
authored
Send proper telemetry when copy needed files (#69)
* Move file copy telemetry inside LocalSSHService * fix compile * Respond with correct error message when exception * Add failure code to copy file telemetry * downgrade configcat * Wrap errors * 💄 * 💄 * 💄
1 parent f8f747e commit 67f1a3e

File tree

11 files changed

+92
-39
lines changed

11 files changed

+92
-39
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"displayName": "Gitpod",
44
"description": "Gitpod Support",
55
"publisher": "gitpod",
6-
"version": "0.0.112",
6+
"version": "0.0.112",
77
"daemonVersion": "0.0.9",
88
"license": "MIT",
99
"icon": "resources/gitpod.png",

src/common/files.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,15 @@ export async function isFile(path: string) {
2626
}
2727
}
2828

29+
export async function isDir(path: string) {
30+
try {
31+
const s = await fs.promises.stat(path);
32+
return s.isDirectory();
33+
} catch {
34+
return false;
35+
}
36+
}
37+
2938
export function untildify(path: string){
3039
return path.replace(/^~(?=$|\/|\\)/, homeDir);
3140
}

src/common/utils.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,3 +124,16 @@ export function arrayEquals<T>(one: ReadonlyArray<T> | undefined, other: Readonl
124124
export function getServiceURL(gitpodHost: string): string {
125125
return new URL(gitpodHost).toString().replace(/\/$/, '');
126126
}
127+
128+
export class WrapError extends Error {
129+
code: string | undefined;
130+
constructor(msg: string, err: any, code?: string) {
131+
super(msg);
132+
if (err instanceof Error) {
133+
this.name = err.name;
134+
this.stack = err.stack;
135+
this.message = `${msg}: ${err.message}`;
136+
}
137+
this.code = code ? code : err.code;
138+
}
139+
}

src/configuration.ts

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,7 @@ function getGitpodHost() {
1313
return vscode.workspace.getConfiguration('gitpod').get<string>('host', 'https://gitpod.io/');
1414
}
1515

16-
function getUseLocalApp(useLocalSSHServer?: boolean) {
17-
if (useLocalSSHServer) {
18-
return false;
19-
}
16+
function getUseLocalApp() {
2017
return vscode.workspace.getConfiguration('gitpod').get<boolean>('remote.useLocalApp', false);
2118
}
2219

src/experiments.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,6 @@ export class ExperimentalSettings extends Disposable {
6464
async getRaw<T>(
6565
configcatKey: string,
6666
custom: {
67-
gitpodHost: string;
6867
[key: string]: string;
6968
}
7069
) {
@@ -140,8 +139,8 @@ export class ExperimentalSettings extends Disposable {
140139
/**
141140
* @see https://app.configcat.com/08da1258-64fb-4a8e-8a1e-51de773884f6/08da1258-6541-4fc7-8b61-c8b47f82f3a0/08da1258-6512-4ec0-80a3-3f6aa301f853?settingId=75503
142141
*/
143-
async getUseLocalSSHProxy(gitpodHost: string): Promise<boolean> {
144-
return (await this.getRaw<boolean>('gitpod_desktop_use_local_ssh_proxy', { gitpodHost })) ?? false;
142+
async getUseLocalSSHProxy(): Promise<boolean> {
143+
return (await this.getRaw<boolean>('gitpod_desktop_use_local_ssh_proxy', {})) ?? false;
145144
}
146145

147146
async getUsePublicAPI(gitpodHost: string): Promise<boolean> {

src/extension.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,12 +72,12 @@ export async function activate(context: vscode.ExtensionContext) {
7272
hostService = new HostService(context, notificationService, logger);
7373
context.subscriptions.push(hostService);
7474

75-
const localSSHService = new LocalSSHService(context, hostService, telemetryService, logger);
76-
context.subscriptions.push(localSSHService);
77-
7875
const sessionService = new SessionService(hostService, logger);
7976
context.subscriptions.push(sessionService);
8077

78+
const localSSHService = new LocalSSHService(context, hostService, telemetryService, sessionService, logger);
79+
context.subscriptions.push(localSSHService);
80+
8181
const experiments = new ExperimentalSettings(packageJSON.configcatKey, packageJSON.version, context, sessionService, hostService, logger);
8282
context.subscriptions.push(experiments);
8383

src/local-ssh/ipc/extensionServiceServer.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ class ExtensionServiceImpl implements ExtensionServiceImplementation {
112112
};
113113
} catch (e) {
114114
this.logService.error(e, 'failed to get workspace auth info');
115-
throw e;
115+
throw new ServerError(Status.UNAVAILABLE, e.toString());
116116
}
117117
}
118118

src/remoteConnector.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -687,10 +687,12 @@ export class RemoteConnector extends Disposable {
687687
this.usePublicApi = await this.experiments.getUsePublicAPI(params.gitpodHost);
688688
this.logService.info(`Going to use ${this.usePublicApi ? 'public' : 'server'} API`);
689689

690-
let useLocalSSH = await this.experiments.getUseLocalSSHProxy(params.gitpodHost);
690+
let useLocalSSH = await this.experiments.getUseLocalSSHProxy();
691691
if (useLocalSSH) {
692+
// we need to update the remote ssh config first, since another call is too late for local-ssh
693+
await this.updateRemoteSSHConfig(true, undefined);
694+
this.localSSHService.flow = sshFlow;
692695
await this.localSSHService.initialized;
693-
this.telemetryService.sendUserFlowStatus(this.localSSHService.isSupportLocalSSH ? 'success' : 'failure', { ...sshFlow, flow: 'local_ssh_config' });
694696
if (!this.localSSHService.isSupportLocalSSH) {
695697
this.logService.error('Local SSH is not supported on this platform');
696698
useLocalSSH = false;
@@ -700,7 +702,7 @@ export class RemoteConnector extends Disposable {
700702
this.logService.info('Going to use lssh');
701703
}
702704

703-
const forceUseLocalApp = Configuration.getUseLocalApp(useLocalSSH);
705+
const forceUseLocalApp = Configuration.getUseLocalApp();
704706
const userOverride = String(isUserOverrideSetting('gitpod.remote.useLocalApp'));
705707
let sshDestination: SSHDestination | undefined;
706708
if (!forceUseLocalApp) {
@@ -828,7 +830,7 @@ export class RemoteConnector extends Disposable {
828830

829831
public async autoTunnelCommand(gitpodHost: string, instanceId: string, enabled: boolean) {
830832
if (this.sessionService.isSignedIn()) {
831-
const forceUseLocalApp = Configuration.getUseLocalApp(await this.experiments.getUseLocalSSHProxy(gitpodHost));
833+
const forceUseLocalApp = Configuration.getUseLocalApp();
832834
if (!forceUseLocalApp) {
833835
const authority = vscode.Uri.parse(gitpodHost).authority;
834836
const configKey = `config/${authority}`;

src/remoteSession.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ export class RemoteSession extends Disposable {
6161
}
6262

6363
try {
64-
const useLocalSSH = await this.experiments.getUseLocalSSHProxy(this.connectionInfo.gitpodHost);
64+
const useLocalSSH = await this.experiments.getUseLocalSSHProxy();
6565
if (useLocalSSH) {
6666
this.extensionServiceServer = new ExtensionServiceServer(this.logService, this.sessionService, this.hostService, this.telemetryService, this.experiments);
6767
}

src/services/localSSHService.ts

Lines changed: 32 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -12,21 +12,28 @@ import { IHostService } from './hostService';
1212
import SSHConfiguration from '../ssh/sshConfig';
1313
import { isWindows } from '../common/platform';
1414
import { getLocalSSHDomain } from '../remote';
15-
import { ITelemetryService } from './telemetryService';
15+
import { ITelemetryService, UserFlowTelemetry } from './telemetryService';
16+
import { ISessionService } from './sessionService';
17+
import { WrapError } from '../common/utils';
1618

1719
export interface ILocalSSHService {
20+
flow?: UserFlowTelemetry;
1821
isSupportLocalSSH: boolean;
1922
initialized: Promise<void>;
2023
}
2124

25+
type FailedToInitializeCode = 'Unknown' | string;
26+
2227
export class LocalSSHService extends Disposable implements ILocalSSHService {
2328
public isSupportLocalSSH: boolean = false;
2429
public initialized: Promise<void>;
30+
public flow?: UserFlowTelemetry;
2531
constructor(
2632
private readonly context: vscode.ExtensionContext,
2733
private readonly hostService: IHostService,
2834
private readonly telemetryService: ITelemetryService,
29-
private readonly logService: ILogService
35+
private readonly sessionService: ISessionService,
36+
private readonly logService: ILogService,
3037
) {
3138
super();
3239

@@ -53,18 +60,26 @@ export class LocalSSHService extends Disposable implements ILocalSSHService {
5360
}
5461

5562
private async initialize() {
63+
let failureCode: FailedToInitializeCode | undefined;
64+
const useLocalAPP = String(Configuration.getUseLocalApp());
5665
try {
5766
const locations = await this.copyProxyScript();
5867
await this.configureSettings(locations);
5968
this.isSupportLocalSSH = true;
6069
} catch (e) {
61-
this.logService.error(e, 'failed to copy local ssh client.js');
62-
if (e.message) {
63-
e.message = `Failed to copy local ssh client.js: ${e.message}`;
70+
this.logService.error(e, 'failed to initialize');
71+
failureCode = 'Unknown';
72+
if (e?.code) {
73+
failureCode = e.code;
74+
}
75+
if (e?.message) {
76+
e.message = `Failed to initialize: ${e.message}`;
6477
}
65-
this.telemetryService.sendTelemetryException(this.hostService.gitpodHost, e);
78+
this.telemetryService.sendTelemetryException(this.hostService.gitpodHost, e, { useLocalAPP });
6679
this.isSupportLocalSSH = false;
6780
}
81+
const flowData = this.flow ? this.flow : { gitpodHost: this.hostService.gitpodHost, userId: this.sessionService.safeGetUserId() };
82+
this.telemetryService.sendUserFlowStatus(this.isSupportLocalSSH ? 'success' : 'failure', { ...flowData, flow: 'local_ssh_config', failureCode, useLocalAPP });
6883
}
6984

7085
private async configureSettings({ proxyScript, launcher }: { proxyScript: string; launcher: string }) {
@@ -85,14 +100,18 @@ export class LocalSSHService extends Disposable implements ILocalSSHService {
85100
}
86101

87102
private async copyProxyScript() {
88-
const [proxyScript, launcher] = await Promise.all([
89-
this.copyFileToGlobalStorage('out/local-ssh/proxy.js', 'sshproxy/proxy.js'),
90-
isWindows ? this.copyFileToGlobalStorage('out/local-ssh/proxylauncher.bat', 'sshproxy/proxylauncher.bat') : this.copyFileToGlobalStorage('out/local-ssh/proxylauncher.sh', 'sshproxy/proxylauncher.sh')
91-
]);
92-
if (!isWindows) {
93-
await fsp.chmod(launcher, 0o755);
103+
try {
104+
const [proxyScript, launcher] = await Promise.all([
105+
this.copyFileToGlobalStorage('out/local-ssh/proxy.js', 'sshproxy/proxy.js'),
106+
isWindows ? this.copyFileToGlobalStorage('out/local-ssh/proxylauncher.bat', 'sshproxy/proxylauncher.bat') : this.copyFileToGlobalStorage('out/local-ssh/proxylauncher.sh', 'sshproxy/proxylauncher.sh')
107+
]);
108+
if (!isWindows) {
109+
await fsp.chmod(launcher, 0o755);
110+
}
111+
return { proxyScript, launcher };
112+
} catch (e) {
113+
throw new WrapError('Failed to copy local ssh proxy scripts', e);
94114
}
95-
return { proxyScript, launcher };
96115
}
97116

98117
private async copyFileToGlobalStorage(filepath: string, destPath: string) {

0 commit comments

Comments
 (0)