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

Commit

Permalink
Eclipse Che changes #2328, #2344, #2336, #2337 for v2 branch (#2373)
Browse files Browse the repository at this point in the history
* Automatically create Fablet environment when available (resolves #2328)

Signed-off-by: Simon Stone <[email protected]>

* Fix home directory references and store serialization (contributes to #2344)

Signed-off-by: Simon Stone <[email protected]>

* On Eclipse Che, use projects directory instead of home directory (resolves #2344)

Signed-off-by: Simon Stone <[email protected]>

* Stop duplicating Eclipse Che checks everywhere (contributes to #2336)

Signed-off-by: Simon Stone <[email protected]>

* Automatically select all nodes when on Eclipse Che (resolves #2336)

Signed-off-by: Simon Stone <[email protected]>

* Determine node label before node creation (resolves #2337)

Signed-off-by: Simon Stone <[email protected]>

* Fix compile errors

Signed-off-by: Simon Stone <[email protected]>

Co-authored-by: Caroline Fletcher <[email protected]>
  • Loading branch information
Simon Stone and cazfletch authored May 26, 2020
1 parent b430cef commit 7cde1e3
Show file tree
Hide file tree
Showing 18 changed files with 334 additions and 69 deletions.
7 changes: 6 additions & 1 deletion packages/blockchain-common/src/util/FileSystemUtil.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,12 @@ export class FileSystemUtil {
*/
public static getDirPath(dir: string): string {
if (dir.startsWith('~')) {
dir = homeDir(dir.replace('~', ''));
const CHE_PROJECTS_ROOT: string = process.env.CHE_PROJECTS_ROOT;
if (CHE_PROJECTS_ROOT) {
dir = dir.replace('~', CHE_PROJECTS_ROOT);
} else {
dir = homeDir(dir.replace('~', ''));
}
}
return dir;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,10 @@ describe('FabletClient', () => {
});
});

afterEach(() => {
mockAxios.restore();
});

describe('#getComponents', () => {

it('should get the list of components', async () => {
Expand Down
16 changes: 16 additions & 0 deletions packages/blockchain-common/test/util/FileSystemUtil.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,28 @@ chai.use(sinonChai);
// tslint:disable no-unused-expression
describe('FileSystemUtil', () => {
describe('getDirPath', () => {

beforeEach(() => {
delete process.env.CHE_PROJECTS_ROOT;
});

afterEach(() => {
delete process.env.CHE_PROJECTS_ROOT;
});

it('should replace ~ with the users home directory', () => {
const packageDirOriginal: string = '~/smartContractDir';
const packageDirNew: string = FileSystemUtil.getDirPath(packageDirOriginal);
packageDirNew.should.not.contain('~');
});

it('should replace ~ with the users projects directory on Eclipse Che', () => {
process.env.CHE_PROJECTS_ROOT = '/projects';
const packageDirOriginal: string = '~/smartContractDir';
const packageDirNew: string = FileSystemUtil.getDirPath(packageDirOriginal);
packageDirNew.should.equal('/projects/smartContractDir');
});

it('should not replace if not ~', () => {
const packageDirOriginal: string = '/banana/smartContractDir';
const packageDirNew: string = FileSystemUtil.getDirPath(packageDirOriginal);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import { EnvironmentFactory } from '../fabric/environments/EnvironmentFactory';
import { TimerUtil } from '../util/TimerUtil';
import { LocalEnvironment } from '../fabric/environments/LocalEnvironment';
import { LocalEnvironmentManager } from '../fabric/environments/LocalEnvironmentManager';
import { ExtensionUtil } from '../util/ExtensionUtil';

export interface IBlockchainQuickPickItem<T = undefined> extends vscode.QuickPickItem {
data: T;
Expand Down Expand Up @@ -1170,7 +1171,15 @@ export class UserInputUtil {
// stop the refresh until the user has finished making changes
FabricEnvironmentManager.instance().stopEnvironmentRefresh();
// Ask user if they want to edit filters
const editFilters: boolean = await UserInputUtil.showConfirmationWarningMessage('Differences have been detected between the local environment and the Ops Tools environment. Would you like to filter nodes?');
let editFilters: boolean;
if (ExtensionUtil.isChe()) {
// Issue 2336: see below; because we never show the filter quick pick, we don't want
// to show this message either. Just pretend that they said yes, and then went on
// to select all of the nodes,
editFilters = true;
} else {
editFilters = await UserInputUtil.showConfirmationWarningMessage('Differences have been detected between the local environment and the Ops Tools environment. Would you like to filter nodes?');
}
if (!editFilters) {
return;
}
Expand All @@ -1187,6 +1196,14 @@ export class UserInputUtil {
placeHolder: prompt
};

if (ExtensionUtil.isChe()) {
// Issue 2336: Eclipse Che doesn't support canPickMany, and that makes this quick pick pointless
// as 99% of the time you want to pick many. Just return all of the nodes instead for now.
return sortedQuickPickItems.map((sortedQuickPickItem: IBlockchainQuickPickItem<FabricNode>) => {
sortedQuickPickItem.picked = true;
return sortedQuickPickItem;
})
}
return vscode.window.showQuickPick(sortedQuickPickItems, quickPickOptions);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -509,7 +509,7 @@ export class DependencyManager {
let info: { modules?: string, longVersion: string, shortVersion?: string };

const remote: boolean = vscode.extensions.getExtension(EXTENSION_ID).extensionKind === vscode.ExtensionKind.Workspace;
const che: boolean = 'CHE_WORKSPACE_ID' in process.env;
const che: boolean = ExtensionUtil.isChe();

if (remote || che) {
runtime = 'node';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -163,24 +163,29 @@ export class BlockchainEnvironmentExplorerProvider implements BlockchainExplorer
arguments: [environmentRegistryEntry, node]
};

let label: string = node.cluster_name || node.name;
if (!node.wallet || !node.identity) {
label += ' ⚠';
}

if (node.type === FabricNodeType.PEER) {
const peerTreeItem: PeerTreeItem = new PeerTreeItem(this, node.name, node.name, environmentRegistryEntry, node, command);
const peerTreeItem: PeerTreeItem = new PeerTreeItem(this, label, node.name, environmentRegistryEntry, node, command);
tree.push(peerTreeItem);
}

if (node.type === FabricNodeType.CERTIFICATE_AUTHORITY) {
const certificateAuthorityTreeItem: CertificateAuthorityTreeItem = new CertificateAuthorityTreeItem(this, node.name, node.name, environmentRegistryEntry, node, command);
const certificateAuthorityTreeItem: CertificateAuthorityTreeItem = new CertificateAuthorityTreeItem(this, label, node.name, environmentRegistryEntry, node, command);
tree.push(certificateAuthorityTreeItem);
}

if (node.type === FabricNodeType.ORDERER) {
if (node.cluster_name) {
const foundTreeItem: BlockchainTreeItem = tree.find((treeItem: OrdererTreeItem) => treeItem.node && treeItem.node.type === FabricNodeType.ORDERER && node.cluster_name === treeItem.node.cluster_name);
if (!foundTreeItem) {
tree.push(new OrdererTreeItem(this, node.cluster_name, node.cluster_name, environmentRegistryEntry, node, command));
tree.push(new OrdererTreeItem(this, label, node.cluster_name, environmentRegistryEntry, node, command));
}
} else {
tree.push(new OrdererTreeItem(this, node.name, node.name, environmentRegistryEntry, node, command));
tree.push(new OrdererTreeItem(this, label, node.name, environmentRegistryEntry, node, command));
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,29 +21,7 @@ import * as vscode from 'vscode';

export abstract class NodeTreeItem extends BlockchainTreeItem {

private orginalLabel: string;

constructor(provider: BlockchainExplorerProvider, label: string, public readonly tooltip: string, public readonly environmentRegistryEntry: FabricEnvironmentRegistryEntry, public readonly node: FabricNode, public readonly command?: vscode.Command) {
super(provider, label, vscode.TreeItemCollapsibleState.None);

this.orginalLabel = label;

this.updateProperties();
}

protected updateProperties(): void {
let newLabel: string = this.orginalLabel;

if (!this.node.wallet || !this.node.identity) {
newLabel += ' ⚠';
}

this.setLabel(newLabel);
this.refresh();
}

private setLabel(label: string): void {
// label is readonly so make it less readonly
(this as any).label = label;
}
}
62 changes: 61 additions & 1 deletion packages/blockchain-extension/extension/util/ExtensionUtil.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ import { GlobalState, ExtensionData } from './GlobalState';
import { TemporaryCommandRegistry } from '../dependencies/TemporaryCommandRegistry';
import { version as currentExtensionVersion, dependencies } from '../../package.json';
import { UserInputUtil } from '../commands/UserInputUtil';
import { FabricSmartContractDefinition, FabricEnvironmentRegistry, FabricEnvironmentRegistryEntry, FabricNode, FabricRuntimeUtil, FabricWalletRegistry, FabricWalletRegistryEntry, FileRegistry, LogType, FabricGatewayRegistry, FabricGatewayRegistryEntry, EnvironmentType, EnvironmentFlags } from 'ibm-blockchain-platform-common';
import { FabricSmartContractDefinition, FabricEnvironmentRegistry, FabricEnvironmentRegistryEntry, FabricNode, FabricRuntimeUtil, FabricWalletRegistry, FabricWalletRegistryEntry, FileRegistry, LogType, FabricGatewayRegistry, FabricGatewayRegistryEntry, EnvironmentType, EnvironmentFlags, FileSystemUtil, FileConfigurations } from 'ibm-blockchain-platform-common';
import { FabricDebugConfigurationProvider } from '../debug/FabricDebugConfigurationProvider';
import { importNodesToEnvironment } from '../commands/importNodesToEnvironmentCommand';
import { deleteNode } from '../commands/deleteNodeCommand';
Expand All @@ -107,6 +107,8 @@ import { deploySmartContract } from '../commands/deployCommand';
import { openDeployView } from '../commands/openDeployView';
import { saveTutorial } from '../commands/saveTutorialCommand';
import { manageFeatureFlags } from '../commands/manageFeatureFlags';
import Axios from 'axios';
import { URL } from 'url';

let blockchainGatewayExplorerProvider: BlockchainGatewayExplorerProvider;
let blockchainPackageExplorerProvider: BlockchainPackageExplorerProvider;
Expand Down Expand Up @@ -668,6 +670,9 @@ export class ExtensionUtil {
extensionData.generatorVersion = generatorVersion;
await GlobalState.update(extensionData);
}

// Discover any environments and ensure they are available.
await this.discoverEnvironments();
}

/*
Expand Down Expand Up @@ -717,6 +722,61 @@ export class ExtensionUtil {
return vscode.workspace.getConfiguration().get(SettingConfigurations.EXTENSION_SAAS_CONFIG_UPDATES);
}

public static async discoverEnvironments(): Promise<void> {

// First, check to see if we're running in Eclipse Che; currently
// we can only discover environments created by Eclipse Che.
if (ExtensionUtil.isChe()) {
await this.discoverCheEnvironments();
}

}

public static async discoverCheEnvironments(): Promise<void> {

// Check for a Fablet instance running within this Eclipse Che.
const FABLET_SERVICE_HOST: string = process.env['FABLET_SERVICE_HOST'];
const FABLET_SERVICE_PORT: string = process.env['FABLET_SERVICE_PORT'];
if (!FABLET_SERVICE_HOST || !FABLET_SERVICE_PORT) {
return;
}
const url: string = `http://${FABLET_SERVICE_HOST}:${FABLET_SERVICE_PORT}`;

// Try to connect to the Fablet instance.
try {
await Axios.get(new URL('/ak/api/v1/health', url).toString());
} catch (error) {
// This isn't a valid Fablet instance.
return;
}

// Determine where this environment should store any files.
const extensionDirectory: string = vscode.workspace.getConfiguration().get(SettingConfigurations.EXTENSION_DIRECTORY);
const resolvedExtensionDirectory: string = FileSystemUtil.getDirPath(extensionDirectory);
const environmentDirectory: string = path.join(resolvedExtensionDirectory, FileConfigurations.FABRIC_ENVIRONMENTS, 'Fablet');

// Register the Fablet instance.
const environmentRegistry: FabricEnvironmentRegistry = FabricEnvironmentRegistry.instance();
const environmentExists: boolean = await environmentRegistry.exists('Fablet');
const environmentRegistryEntry: FabricEnvironmentRegistryEntry = new FabricEnvironmentRegistryEntry({
name: 'Fablet',
managedRuntime: false,
environmentType: EnvironmentType.FABLET_ENVIRONMENT,
environmentDirectory,
url
});
if (!environmentExists) {
await environmentRegistry.add(environmentRegistryEntry);
} else {
await environmentRegistry.update(environmentRegistryEntry);
}

}

public static isChe(): boolean {
return 'CHE_WORKSPACE_ID' in process.env;
}

private static getExtension(): vscode.Extension<any> {
return vscode.extensions.getExtension(EXTENSION_ID);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ export class FileSystemSecureStore implements SecureStore {

private async save(store: Store): Promise<void> {
const data: string = Buffer.from(JSON.stringify(store), 'utf8').toString('base64');
return fs.writeJson(this.path, data, { encoding: 'utf8', mode: 0o600 });
return fs.writeFile(this.path, data, { encoding: 'utf8', mode: 0o600 });
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { SettingConfigurations } from '../configurations';
import * as path from 'path';
import * as vscode from 'vscode';
import { FileSystemSecureStore } from './FileSystemSecureStore';
import { FileSystemUtil } from 'ibm-blockchain-platform-common';

export class SecureStoreFactory {

Expand All @@ -28,7 +29,8 @@ export class SecureStoreFactory {
return new KeytarSecureStore(keytar);
}
const extensionDirectory: string = vscode.workspace.getConfiguration().get(SettingConfigurations.EXTENSION_DIRECTORY);
const storePath: string = path.join(extensionDirectory, 'ibm-blockchain-platform.store');
const resolvedExtensionDirectory: string = FileSystemUtil.getDirPath(extensionDirectory);
const storePath: string = path.join(resolvedExtensionDirectory, 'ibm-blockchain-platform.store');
return new FileSystemSecureStore(storePath);
}

Expand Down
18 changes: 18 additions & 0 deletions packages/blockchain-extension/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions packages/blockchain-extension/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -1414,6 +1414,7 @@
"@types/tmp": "0.0.33",
"@types/vscode": "1.38.0",
"angular-tslint-rules": "1.5.0",
"axios-mock-adapter": "^1.18.1",
"chai": "4.1.2",
"chai-as-promised": "7.1.1",
"cucumber": "2.2.0",
Expand Down
39 changes: 39 additions & 0 deletions packages/blockchain-extension/test/commands/UserInputUtil.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import { FabricEnvironmentManager } from '../../extension/fabric/environments/Fa
import { LocalEnvironment } from '../../extension/fabric/environments/LocalEnvironment';
import { LocalEnvironmentManager } from '../../extension/fabric/environments/LocalEnvironmentManager';
import { FabricInstalledSmartContract } from 'ibm-blockchain-platform-common/build/src/fabricModel/FabricInstalledSmartContract';
import { ExtensionUtil } from '../../extension/util/ExtensionUtil';

chai.use(sinonChai);
const should: Chai.Should = chai.should();
Expand Down Expand Up @@ -2988,6 +2989,22 @@ describe('UserInputUtil', () => {
stopRefreshStub.should.not.have.been.called;
});

it('should just select all nodes from Ops Tools without asking on Eclipse Che', async () => {
mySandBox.stub(ExtensionUtil, 'isChe').returns(true);
quickPickStub.rejects(new Error('should not be called'));

const result: IBlockchainQuickPickItem<FabricNode>[] = await UserInputUtil.showNodesQuickPickBox('choose your nodes', nodes, true) as IBlockchainQuickPickItem<FabricNode>[];

for (const node of nodesPickWithoutCurrent) {
node.picked = true;
}

result.should.deep.equal(nodesPickWithoutCurrent);
quickPickStub.should.not.have.been.called;
sortNodesForQuickpickStub.should.have.been.called;
stopRefreshStub.should.not.have.been.called;
});

it('should show description "(new)" when a new node is present from Ops Tool', async () => {
const newPeerNode: FabricNode = FabricNode.newPeer('peerNew.org1.example.com', 'peerNew.org1.example.com', 'grps://somehost:7056', 'cake_fabric_wallet', 'admin', 'Org1MSP');
const newNodes: FabricNode[] = Array.from(nodes);
Expand Down Expand Up @@ -3061,6 +3078,28 @@ describe('UserInputUtil', () => {
changeDetectedWarningMessage.should.have.been.called;
stopRefreshStub.should.have.been.called;
});

it('should just select all nodes without asking on Eclipse Che when informOfChanges is true and changes are detected when connecting to Ops Tool', async () => {
mySandBox.stub(ExtensionUtil, 'isChe').returns(true);
const newPeerNode: FabricNode = FabricNode.newPeer('peerNew.org1.example.com', 'peerNew.org1.example.com', 'grps://somehost:7056', 'cake_fabric_wallet', 'admin', 'Org1MSP');
const newNodes: FabricNode[] = Array.from(nodes);
newNodes.push(newPeerNode);
nodesPickWithoutCurrent.push({ label: newPeerNode.name, data: newPeerNode, description: '(new)' });
changeDetectedWarningMessage.resolves(true);
quickPickStub.rejects(new Error('should not be called'));

const result: IBlockchainQuickPickItem<FabricNode>[] = await UserInputUtil.showNodesQuickPickBox('choose your nodes', newNodes, true, nodes, true) as IBlockchainQuickPickItem<FabricNode>[];

for (const node of nodesPickWithoutCurrent) {
node.picked = true;
}

result.should.deep.equal(nodesPickWithoutCurrent);
quickPickStub.should.not.have.been.called;
sortNodesForQuickpickStub.should.have.been.called;
changeDetectedWarningMessage.should.not.have.been.called;
stopRefreshStub.should.have.been.called;
});
});

describe('sortNodesForQuickpick', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,6 @@ describe('DependencyManager Tests', () => {
getExtensionLocalFabricSetting = mySandBox.stub(ExtensionUtil, 'getExtensionLocalFabricSetting').returns(true);
});

afterEach(async () => {
delete process.env.CHE_WORKSPACE_ID;
});

describe('hasNativeDependenciesInstalled', () => {

afterEach(() => {
Expand Down Expand Up @@ -480,7 +476,7 @@ describe('DependencyManager Tests', () => {
extensionKindStub.onThirdCall().returns({ extensionKind: 2 });
mySandBox.stub(process, 'platform').value(platform);
mySandBox.stub(process, 'arch').value(arch);
process.env.CHE_WORKSPACE_ID = 'wowsuchcheworkspaceid';
mySandBox.stub(ExtensionUtil, 'isChe').returns(true);

const sendCommandStub: sinon.SinonStub = mySandBox.stub(CommandUtil, 'sendCommandWithOutput').resolves();
const dependencyManager: DependencyManager = DependencyManager.instance();
Expand Down
Loading

0 comments on commit 7cde1e3

Please sign in to comment.