Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import { VSCodeSettings } from '@redhat-developer/vscode-redhat-telemetry/lib/common/vscode/settings';
import * as cp from 'child_process';
import { CommandText } from './base/command';
import { ToolsConfig } from './tools';
import { ToolNotFoundError, ToolsConfig } from './tools';
import { ChildProcessUtil, CliExitData } from './util/childProcessUtil';
import { ExecutionContext } from './util/utils';
import { VsCommandError } from './vscommand';
Expand Down Expand Up @@ -128,6 +128,9 @@ export class CliChannel {
}

const toolLocation = await ToolsConfig.detect(cmd.command);
if (!toolLocation) {
throw new ToolNotFoundError(cmd.command);
}
const optWithTelemetryEnv = CliChannel.applyEnv(options, CliChannel.createTelemetryEnv()) as cp.ExecFileSyncOptionsWithStringEncoding;

const result: string = cp.execFileSync(toolLocation, [cmd.parameter, ...cmd.options.map((o)=>o.toString())], optWithTelemetryEnv);
Expand Down
7 changes: 7 additions & 0 deletions src/tools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,13 @@ import configData from './tools.json';
import { ChildProcessUtil } from './util/childProcessUtil';
import { Platform } from './util/platform';

export class ToolNotFoundError extends Error {
constructor(public readonly toolName: string) {
super(`'${toolName}' tool not found. Install it or enable 'openshiftToolkit.searchForToolsInPath' setting.`);
this.name = 'ToolNotFoundError';
}
}

export class ToolsConfig {

public static tools: any = ToolsConfig.loadMetadata(configData, Platform.OS);
Expand Down
11 changes: 9 additions & 2 deletions src/util/loginUtil.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import { CommandOption, CommandText } from '../base/command';
import { CliChannel } from '../cli';
import { Oc } from '../oc/ocWrapper';
import { ToolNotFoundError } from '../tools';
import { isOpenShiftCluster } from './kubeUtils';

export class LoginUtil {
Expand Down Expand Up @@ -37,9 +38,15 @@ export class LoginUtil {
return await CliChannel.getInstance().executeSyncTool(
new CommandText('oc', 'api-versions'), { timeout: 5000 })
.then((response) => !response || response.trim().length === 0) // Active user is set - no need to login
.catch((error) => true);
.catch((error) => {
if (error instanceof ToolNotFoundError) throw error;
return true;
});
})
.catch((error) => true); // Can't get server - require to login
.catch((error) => {
if (error instanceof ToolNotFoundError) throw error;
return true;
});
}

/**
Expand Down
25 changes: 25 additions & 0 deletions test/unit/tools.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import sinonChai from 'sinon-chai';
import * as vscode from 'vscode';
import { ChildProcessUtil, CliExitData } from '../../src/util/childProcessUtil';
import { Platform } from '../../src/util/platform';
import { ToolNotFoundError } from '../../src/tools';
import * as utils from '../../src/util/utils';

chai.use(sinonChai);
Expand Down Expand Up @@ -152,4 +153,28 @@ suite('tools configuration', () => {
assert.ok(!config.odo);
});
});

suite('ToolNotFoundError', () => {
test('extends Error', () => {
const error = new ToolNotFoundError('oc');
assert.ok(error instanceof Error);
assert.ok(error instanceof ToolNotFoundError);
});

test('has correct name property', () => {
const error = new ToolNotFoundError('oc');
assert.equal(error.name, 'ToolNotFoundError');
});

test('stores toolName', () => {
const error = new ToolNotFoundError('oc');
assert.equal(error.toolName, 'oc');
});

test('message includes tool name and setting hint', () => {
const error = new ToolNotFoundError('oc');
assert.ok(error.message.includes('\'oc\''));
assert.ok(error.message.includes('searchForToolsInPath'));
});
});
});
84 changes: 84 additions & 0 deletions test/unit/util/loginUtil.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/*-----------------------------------------------------------------------------------------------
* Copyright (c) Red Hat, Inc. All rights reserved.
* Licensed under the MIT License. See LICENSE file in the project root for license information.
*-----------------------------------------------------------------------------------------------*/

import * as chai from 'chai';
import * as sinon from 'sinon';
import sinonChai from 'sinon-chai';
import { CliChannel } from '../../../src/cli';
import { ToolNotFoundError } from '../../../src/tools';
import { LoginUtil } from '../../../src/util/loginUtil';

const { expect } = chai;
chai.use(sinonChai);

suite('util/loginUtil.ts', () => {
let sandbox: sinon.SinonSandbox;
let executeSyncToolStub: sinon.SinonStub;

setup(() => {
sandbox = sinon.createSandbox();
executeSyncToolStub = sandbox.stub(CliChannel.getInstance(), 'executeSyncTool');
});

teardown(() => {
sandbox.restore();
});

suite('requireLogin()', () => {
test('returns false when oc whoami and api-versions succeed', async () => {
executeSyncToolStub.onFirstCall().resolves('https://api.cluster:6443\n');
executeSyncToolStub.onSecondCall().resolves('apps/v1\nv1\n');

const result = await LoginUtil.Instance.requireLogin();
expect(result).to.be.false;
});

test('returns true when oc whoami fails with regular error', async () => {
executeSyncToolStub.rejects(new Error('connection refused'));

const result = await LoginUtil.Instance.requireLogin();
expect(result).to.be.true;
});

test('returns true when api-versions returns empty', async () => {
executeSyncToolStub.onFirstCall().resolves('https://api.cluster:6443\n');
executeSyncToolStub.onSecondCall().resolves('');

const result = await LoginUtil.Instance.requireLogin();
expect(result).to.be.true;
});

test('propagates ToolNotFoundError from oc whoami', async () => {
executeSyncToolStub.rejects(new ToolNotFoundError('oc'));

try {
await LoginUtil.Instance.requireLogin();
expect.fail('should have thrown');
} catch (error) {
expect(error).to.be.instanceOf(ToolNotFoundError);
expect((error as ToolNotFoundError).toolName).to.equal('oc');
}
});

test('propagates ToolNotFoundError from api-versions', async () => {
executeSyncToolStub.onFirstCall().resolves('https://api.cluster:6443\n');
executeSyncToolStub.onSecondCall().rejects(new ToolNotFoundError('oc'));

try {
await LoginUtil.Instance.requireLogin();
expect.fail('should have thrown');
} catch (error) {
expect(error).to.be.instanceOf(ToolNotFoundError);
}
});

test('returns true when server URI does not match', async () => {
executeSyncToolStub.onFirstCall().resolves('https://api.cluster:6443\n');

const result = await LoginUtil.Instance.requireLogin('https://other.cluster:6443');
expect(result).to.be.true;
});
});
});
Loading