Skip to content

Commit d7f9f6f

Browse files
authored
Merge pull request #2857 from codefori/connectionErrorReport
Display connection error message + add detail in output
2 parents fdf6d55 + dff7073 commit d7f9f6f

File tree

6 files changed

+44
-34
lines changed

6 files changed

+44
-34
lines changed

src/api/IBMi.ts

Lines changed: 22 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import * as node_ssh from "node-ssh";
44
import os from "os";
55
import path, { parse as parsePath } from 'path';
66
import { EventEmitter } from 'stream';
7-
import { EditorPath } from './types';
87
import { CompileTools } from "./CompileTools";
98
import IBMiContent from "./IBMiContent";
109
import { Tools } from './Tools';
@@ -16,9 +15,9 @@ import * as configVars from './configVars';
1615
import { DebugConfiguration } from "./configuration/DebugConfiguration";
1716
import { ConnectionManager } from './configuration/config/ConnectionManager';
1817
import { ConnectionConfig, RemoteConfigFile } from './configuration/config/types';
19-
import { CachedServerSettings, CodeForIStorage } from './configuration/storage/CodeForIStorage';
20-
import { AspInfo, CommandData, CommandResult, ConnectionData, IBMiMember, RemoteCommand, WrapResult } from './types';
2118
import { ConfigFile } from './configuration/serverFile';
19+
import { CachedServerSettings, CodeForIStorage } from './configuration/storage/CodeForIStorage';
20+
import { AspInfo, CommandData, CommandResult, ConnectionData, EditorPath, IBMiMember, RemoteCommand, WrapResult } from './types';
2221

2322
export interface MemberParts extends IBMiMember {
2423
basename: string
@@ -28,8 +27,9 @@ export type ConnectionMessageType = 'info' | 'warning' | 'error';
2827
export type ConnectionErrorCode = `shell_config` | `home_directory_creation` | `QCPTOIMPF_exists` | `QCPFRMIMPF_exists` | `default_not_bash` | `invalid_bashrc` | `invalid_temp_lib` | `no_auto_conv_ebcdic` | `not_loaded_debug_config` | `no_sql_runner` | `ccsid_warning`;
2928

3029
export interface ConnectionResult {
31-
success: boolean,
32-
errorCodes?: ConnectionErrorCode[],
30+
success: boolean
31+
error?: string
32+
errorCodes?: ConnectionErrorCode[]
3333
}
3434

3535
const remoteApps = [ // All names MUST also be defined as key in 'remoteFeatures' below!!
@@ -122,7 +122,7 @@ export default class IBMi {
122122
* the root of the IFS, thus why we store it.
123123
*/
124124
private iAspInfo: AspInfo[] = [];
125-
private currentAsp: string|undefined;
125+
private currentAsp: string | undefined;
126126
private libraryAsps = new Map<string, number>();
127127

128128
/**
@@ -273,7 +273,8 @@ export default class IBMi {
273273
}
274274
await this.client.connect({
275275
...connectionObject,
276-
privateKeyPath: connectionObject.privateKeyPath ? Tools.resolvePath(connectionObject.privateKeyPath) : undefined
276+
privateKeyPath: connectionObject.privateKeyPath ? Tools.resolvePath(connectionObject.privateKeyPath) : undefined,
277+
debug: connectionObject.sshDebug ? (message: string) => this.appendOutput(`\n[SSH debug] ${message}`) : undefined
277278
} as node_ssh.Config);
278279

279280
let wasCancelled = false;
@@ -990,16 +991,22 @@ export default class IBMi {
990991

991992
let error = e.message;
992993
if (e.code === "ENOTFOUND") {
993-
error = `Host is unreachable. Check the connection's hostname/IP address.`;
994+
error = `host is unreachable. Check the connection's hostname/IP address.`;
994995
}
995996
else if (e.code === "ECONNREFUSED") {
996-
error = `Port ${connectionObject.port} is unreachable. Check the connection's port number or run command STRTCPSVR SERVER(*SSHD) on the host.`
997+
error = `port ${connectionObject.port} is unreachable. Check the connection's port number or run command STRTCPSVR SERVER(*SSHD) on the host.`
997998
}
998999
else if (e.level === "client-authentication") {
999-
error = `Check your credentials${e.message ? ` (${e.message})` : ''}.`;
1000+
error = `check your credentials${e.message ? ` (${e.message})` : ''}.`;
1001+
}
1002+
1003+
this.appendOutput(`${JSON.stringify(e)}`);
1004+
if (typeof e.stack === "string") {
1005+
this.appendOutput(`\n\n${e.stack}`);
10001006
}
10011007

10021008
return {
1009+
error,
10031010
success: false
10041011
};
10051012
}
@@ -1523,7 +1530,7 @@ export default class IBMi {
15231530
return this.remoteFeatures[`startDebugService.sh`] !== undefined;
15241531
}
15251532

1526-
private async getUserProfileAsp(): Promise<string|undefined> {
1533+
private async getUserProfileAsp(): Promise<string | undefined> {
15271534
const [currentRdb] = await this.runSQL(`values current_server`);
15281535

15291536
if (currentRdb) {
@@ -1541,8 +1548,8 @@ export default class IBMi {
15411548
return this.iAspInfo;
15421549
}
15431550

1544-
getIAspDetail(by: string|number) {
1545-
let asp: AspInfo|undefined;
1551+
getIAspDetail(by: string | number) {
1552+
let asp: AspInfo | undefined;
15461553
if (typeof by === 'string') {
15471554
asp = this.iAspInfo.find(asp => asp.name === by);
15481555
} else {
@@ -1554,14 +1561,14 @@ export default class IBMi {
15541561
}
15551562
}
15561563

1557-
getIAspName(by: string|number): string|undefined {
1564+
getIAspName(by: string | number): string | undefined {
15581565
return this.getIAspDetail(by)?.name;
15591566
}
15601567

15611568
getCurrentIAspName() {
15621569
return this.currentAsp;
15631570
}
1564-
async lookupLibraryIAsp(library: string): Promise<string|undefined> {
1571+
async lookupLibraryIAsp(library: string): Promise<string | undefined> {
15651572
library = this.upperCaseName(library);
15661573
let foundNumber = this.libraryAsps.get(library);
15671574

src/api/tests/connection.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ export async function newConnection(reloadSettings?: boolean) {
102102
}
103103

104104
if (!result.success) {
105-
throw new Error(`Failed to connect to IBMi`);
105+
throw new Error(`Failed to connect to IBMi${result.error ? `: ${result.error}` : '!'}`);
106106
}
107107

108108
return conn;

src/api/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ export interface ConnectionData {
6565
privateKeyPath?: string;
6666
keepaliveInterval?: number;
6767
readyTimeout?: number;
68+
sshDebug?: boolean;
6869
}
6970

7071
export interface Server {

src/commands/connection.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,26 @@
11
import { commands, Disposable, ExtensionContext, window } from "vscode";
2+
import { ConnectionResult } from "../api/IBMi";
3+
import { setStoredPassword } from "../config/passwords";
24
import Instance from "../Instance";
3-
import { ConnectionData } from "../typings";
45
import { safeDisconnect } from "../instantiate";
5-
import { setStoredPassword } from "../config/passwords";
6+
import { ConnectionData } from "../typings";
67

78
export function registerConnectionCommands(context: ExtensionContext, instance: Instance): Disposable[] {
89

910
return [
1011
commands.registerCommand(`code-for-ibmi.connectDirect`,
11-
async (connectionData: ConnectionData, reloadSettings = false, savePassword = false): Promise<boolean> => {
12+
async (connectionData: ConnectionData, reloadSettings = false, savePassword = false): Promise<ConnectionResult | undefined> => {
1213
const existingConnection = instance.getConnection();
1314

1415
if (existingConnection) {
15-
return false;
16+
return;
1617
}
1718

1819
if (savePassword && connectionData.password) {
1920
await setStoredPassword(context, connectionData.name, connectionData.password);
2021
}
2122

22-
return (await instance.connect({data: connectionData, reloadServerSettings: reloadSettings})).success;
23+
return (await instance.connect({ data: connectionData, reloadServerSettings: reloadSettings }));
2324
}
2425
),
2526
commands.registerCommand(`code-for-ibmi.disconnect`, async (silent?: boolean) => {

src/webviews/login/index.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import vscode, { l10n, ThemeIcon } from "vscode";
2-
import { CustomUI, Section } from "../CustomUI";
2+
import IBMi from "../../api/IBMi";
33
import { Tools } from "../../api/Tools";
4+
import { deleteStoredPassword, getStoredPassword, setStoredPassword } from "../../config/passwords";
45
import { instance, safeDisconnect } from "../../instantiate";
56
import { ConnectionData } from '../../typings';
6-
import IBMi from "../../api/IBMi";
7-
import { deleteStoredPassword, getStoredPassword, setStoredPassword } from "../../config/passwords";
7+
import { CustomUI, Section } from "../CustomUI";
88

99
type NewLoginSettings = ConnectionData & {
1010
savePassword: boolean
@@ -36,8 +36,8 @@ export class Login {
3636
.addCheckbox(`savePassword`, l10n.t(`Save Password`))
3737
.addFile(`privateKeyPath`, l10n.t(`Private Key`), l10n.t(`OpenSSH, RFC4716 and PPK formats are supported.`))
3838
.addHorizontalRule()
39-
.addInput(`readyTimeout`, l10n.t(`Connection Timeout (in milliseconds)`), l10n.t(`How long to wait for the SSH handshake to complete.`), { inputType: "number", min: 1, default: "20000" });
40-
39+
.addInput(`readyTimeout`, l10n.t(`Connection Timeout (in milliseconds)`), l10n.t(`How long to wait for the SSH handshake to complete.`), { inputType: "number", min: 1, default: "20000" })
40+
.addCheckbox(`sshDebug`, l10n.t(`Turn on SSH debug output`), l10n.t(`Enable this to output debug traces in the Code for i and help diagnose SSH connection issues.`));
4141
const tempTab = new Section()
4242
.addInput(`tempLibrary`, `Temporary library`, `Temporary library. Cannot be QTEMP.`, { default: `ILEDITOR`, minlength: 1, maxlength: 10 })
4343
.addInput(`tempDir`, `Temporary IFS directory`, `Directory that will be used to write temporary files to. User must be authorized to create new files in this directory.`, { default: '/tmp', minlength: 1 });
@@ -120,7 +120,7 @@ export class Login {
120120
}
121121

122122
} else {
123-
vscode.window.showErrorMessage(`Not connected to ${data.host}!`);
123+
vscode.window.showErrorMessage(`Not connected to ${data.host}${connected.error ? `: ${connected.error}` : '!'}`);
124124
}
125125
} catch (e) {
126126
vscode.window.showErrorMessage(`Error connecting to ${data.host}! ${e}`);
@@ -180,7 +180,7 @@ export class Login {
180180
if (connected.success) {
181181
vscode.window.showInformationMessage(`Connected to ${connectionConfig.host}!`);
182182
} else {
183-
vscode.window.showErrorMessage(`Not connected to ${connectionConfig.host}!`);
183+
vscode.window.showErrorMessage(`Not connected to ${connectionConfig.host}${connected.error ? `: ${connected.error}` : '!'}`);
184184
}
185185

186186
return true;

src/webviews/settings/index.ts

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ export class SettingsUI {
4040
const passwordAuthorisedExtensions = instance.getStorage()?.getAuthorisedExtensions() || [];
4141

4242
let config: ConnectionConfig;
43-
let serverConfig: RemoteConfigFile|undefined;
43+
let serverConfig: RemoteConfigFile | undefined;
4444

4545
if (connectionSettings && server) {
4646
config = await IBMi.connectionManager.load(server.name);
@@ -68,7 +68,7 @@ export class SettingsUI {
6868
if (serverConfig && serverConfig.codefori) {
6969
for (const field of currentSection.fields) {
7070
if (!field.id) continue;
71-
71+
7272
if (serverConfig.codefori[field.id] !== undefined) {
7373
field.readonly = true;
7474
}
@@ -204,7 +204,7 @@ export class SettingsUI {
204204
.set("Debug port", config.debugPort);
205205

206206
debugServiceConfig.set("SEP debug port", config.debugSepPort)
207-
207+
208208
debuggerTab.addParagraph(`<ul>${Array.from(debugServiceConfig.entries()).map(([label, value]) => `<li><code>${label}</code>: ${value}</li>`).join("")}</ul>`);
209209

210210
debuggerTab.addCheckbox(`debugUpdateProductionFiles`, `Update production files`, `Determines whether the job being debugged can update objects in production (<code>*PROD</code>) libraries.`, config.debugUpdateProductionFiles)
@@ -395,7 +395,8 @@ export class SettingsUI {
395395
.addPassword(`password`, `${vscode.l10n.t(`Password`)}${storedPassword ? ` (${vscode.l10n.t(`stored`)})` : ``}`, vscode.l10n.t("Only provide a password if you want to update an existing one or set a new one."))
396396
.addFile(`privateKeyPath`, `${vscode.l10n.t(`Private Key`)}${privateKeyPath ? ` (${vscode.l10n.t(`Private Key`)}: ${privateKeyPath})` : ``}`, privateKeyWarning + vscode.l10n.t("Only provide a private key if you want to update from the existing one or set one.") + '<br />' + vscode.l10n.t("OpenSSH, RFC4716 and PPK formats are supported."))
397397
.addHorizontalRule()
398-
.addInput(`readyTimeout`, vscode.l10n.t(`Connection Timeout (in milliseconds)`), vscode.l10n.t(`How long to wait for the SSH handshake to complete.`), { inputType: "number", min: 1, default: stored.readyTimeout ? String(stored.readyTimeout) : "20000" })
398+
.addInput(`readyTimeout`, vscode.l10n.t(`Connection Timeout (in milliseconds)`), vscode.l10n.t(`How long to wait for the SSH handshake to complete.`), { inputType: "number", min: 1, default: stored.readyTimeout ? String(stored.readyTimeout) : "20000" })
399+
.addCheckbox(`sshDebug`, vscode.l10n.t(`Turn on SSH debug output`), vscode.l10n.t(`Enable this to output debug traces in the Code for i and help diagnose SSH connection issues.`), stored.sshDebug)
399400
.addButtons(
400401
{ id: `submitButton`, label: vscode.l10n.t(`Save`), requiresValidation: true },
401402
{ id: `removeAuth`, label: vscode.l10n.t(`Remove auth methods`) }
@@ -477,9 +478,9 @@ function installComponentsQuickPick(connection: IBMi) {
477478
placeHolder: `Select component${withS} to install`
478479
}).then(async result => {
479480
if (result) {
480-
window.withProgress({title: `Component${withS}`, location: vscode.ProgressLocation.Notification}, async (progress) => {
481+
window.withProgress({ title: `Component${withS}`, location: vscode.ProgressLocation.Notification }, async (progress) => {
481482
for (const item of result) {
482-
progress.report({message: `Installing ${item.label}...`});
483+
progress.report({ message: `Installing ${item.label}...` });
483484
try {
484485
await connection.getComponentManager().installComponent(item.id);
485486
} catch (e) {

0 commit comments

Comments
 (0)