'use strict' ; import Axios from 'axios' ; import * as https from 'https' ; import * as vscode from 'vscode' ; import * as path from 'path' ; import { UserInputUtil , IBlockchainQuickPickItem } from './UserInputUtil' ; import { Reporter } from '../util/Reporter' ; import { VSCodeBlockchainOutputAdapter } from '../logging/VSCodeBlockchainOutputAdapter' ; import { FabricEnvironmentRegistry , FabricEnvironmentRegistryEntry , LogType , EnvironmentType , FabricEnvironment , FabricNode , FabricRuntimeUtil , FileSystemUtil , FileConfigurations } from 'ibm-blockchain-platform-common' ; import { ExtensionCommands } from '../../ExtensionCommands' ; import { EnvironmentFactory } from '../fabric/environments/EnvironmentFactory' ; import { SettingConfigurations } from '../../extension/configurations' ; import { ExtensionUtil } from '../util/ExtensionUtil' ; import { GlobalState , ExtensionData } from '../util/GlobalState' ; import { ExtensionsInteractionUtil } from '../util/ExtensionsInteractionUtil' ; import { SecureStore } from '../util/SecureStore' ; import { SecureStoreFactory } from '../util/SecureStoreFactory' ; import { LocalMicroEnvironmentManager } from '../fabric/environments/LocalMicroEnvironmentManager' ; export async function addEnvironment ( ) { const outputAdapter = VSCodeBlockchainOutputAdapter . instance ( ) ; const fabricEnvironmentEntry = new FabricEnvironmentRegistryEntry ( ) ; const fabricEnvironmentRegistry = FabricEnvironmentRegistry . instance ( ) ; let createMethod ; try { outputAdapter . log ( LogType . INFO , undefined , 'Add environment' ) ; const items = [ { label : UserInputUtil . ADD_ENVIRONMENT_FROM_OPS_TOOLS , data : UserInputUtil . ADD_ENVIRONMENT_FROM_OPS_TOOLS , description : UserInputUtil . ADD_ENVIRONMENT_FROM_OPS_TOOLS_DESCRIPTION } , { label : UserInputUtil . ADD_ENVIRONMENT_FROM_NODES , data : UserInputUtil . ADD_ENVIRONMENT_FROM_NODES , description : UserInputUtil . ADD_ENVIRONMENT_FROM_NODES_DESCRIPTION } , { label : UserInputUtil . ADD_ENVIRONMENT_FROM_MICROFAB , data : UserInputUtil . ADD_ENVIRONMENT_FROM_MICROFAB , description : UserInputUtil . ADD_ENVIRONMENT_FROM_MICROFAB_DESCRIPTION } ] ; const localFabricEnabled = ExtensionUtil . getExtensionLocalFabricSetting ( ) ; if ( localFabricEnabled ) { items . unshift ( { label : UserInputUtil . ADD_ENVIRONMENT_FROM_TEMPLATE , data : UserInputUtil . ADD_ENVIRONMENT_FROM_TEMPLATE , description : UserInputUtil . ADD_ENVIRONMENT_FROM_TEMPLATE_DESCRIPTION } ) ; } const chosenMethod = await UserInputUtil . showQuickPickItem ( 'Select a method to add an environment' , items ) as IBlockchainQuickPickItem < string > ; let envDir ; if ( ! chosenMethod ) { return ; } createMethod = chosenMethod . data ; let configurationChosen ; let defaultName = '' ; if ( createMethod === UserInputUtil . ADD_ENVIRONMENT_FROM_TEMPLATE ) { const templateItems = [ { label : UserInputUtil . ONE_ORG_TEMPLATE , data : 1 } , { label : UserInputUtil . TWO_ORG_TEMPLATE , data : 2 } , { label : UserInputUtil . CREATE_ADDITIONAL_LOCAL_NETWORKS , data : UserInputUtil . CREATE_ADDITIONAL_LOCAL_NETWORKS_DATA } ] ; const chosenTemplate = await UserInputUtil . showQuickPickItem ( 'Choose a configuration for a new local network' , templateItems ) as IBlockchainQuickPickItem < number > ; if ( ! chosenTemplate ) { return ; } configurationChosen = chosenTemplate . data ; if ( configurationChosen === UserInputUtil . CREATE_ADDITIONAL_LOCAL_NETWORKS_DATA ) { await vscode . commands . executeCommand ( ExtensionCommands . OPEN_TUTORIAL_PAGE , 'Other tutorials' , 'Create and use a custom Fabric network' ) ; return ; } } else if ( createMethod === UserInputUtil . ADD_ENVIRONMENT_FROM_OPS_TOOLS ) { const isSaaS = await UserInputUtil . showQuickPickYesNo ( 'Are you connecting to an IBM Blockchain Platform service instance on IBM Cloud?' ) ; if ( ! isSaaS ) { return ; } else if ( isSaaS === UserInputUtil . NO ) { const url = await getOpsToolsAccessInfo ( ) ; if ( ! url ) { return ; } fabricEnvironmentEntry . url = url ; fabricEnvironmentEntry . environmentType = EnvironmentType . OPS_TOOLS_ENVIRONMENT ; } else { const accessInfo = await getOpsToolsAccessInfoSaaS ( ) ; if ( ! accessInfo ) { return ; } fabricEnvironmentEntry . url = accessInfo [ 0 ] ; fabricEnvironmentEntry . environmentType = EnvironmentType . SAAS_OPS_TOOLS_ENVIRONMENT ; defaultName = accessInfo [ 1 ] ; } } else if ( createMethod === UserInputUtil . ADD_ENVIRONMENT_FROM_MICROFAB ) { const url = await UserInputUtil . showInputBox ( 'Enter the URL of the Microfab network you want to connect to' , 'http://console.127-0-0-1.nip.io:8080' ) ; if ( ! url ) { return ; } fabricEnvironmentEntry . url = url ; fabricEnvironmentEntry . environmentType = EnvironmentType . MICROFAB_ENVIRONMENT ; } else { fabricEnvironmentEntry . environmentType = EnvironmentType . ENVIRONMENT ; } const environmentName = await UserInputUtil . showInputBox ( 'Enter a name for the environment' , defaultName ) ; if ( ! environmentName ) { return ; } const allEnvironments = await fabricEnvironmentRegistry . getAll ( [ ] ) ; const dockerName = environmentName . replace ( /[^A-Za-z0-9]/g , '' ) ; for ( const _environment of allEnvironments ) { const _environmentDockerName = _environment . name . replace ( /[^A-Za-z0-9]/g , '' ) ; if ( _environment . name === environmentName || _environmentDockerName === dockerName ) { throw new Error ( 'An environment with this name already exists or is too similar.' ) ; } } let fabricCapabilities ; if ( createMethod === UserInputUtil . ADD_ENVIRONMENT_FROM_TEMPLATE ) { fabricCapabilities = await UserInputUtil . selectCapabilities ( 'Select the channel capability version to use for the network' ) ; if ( ! fabricCapabilities ) { return ; } } await vscode . window . withProgress ( { location : vscode . ProgressLocation . Notification , title : 'Blockchain Extension' , cancellable : false } , async ( progress ) : Promise < void > => { progress . report ( { message : `Adding ${ environmentName } environment` } ) ; fabricEnvironmentEntry . name = environmentName ; if ( createMethod === UserInputUtil . ADD_ENVIRONMENT_FROM_TEMPLATE ) { await LocalMicroEnvironmentManager . instance ( ) . initialize ( environmentName , configurationChosen , fabricCapabilities ) ; const environmentEntry = await FabricEnvironmentRegistry . instance ( ) . get ( environmentName ) ; await vscode . commands . executeCommand ( ExtensionCommands . START_FABRIC , environmentEntry ) ; if ( environmentName === FabricRuntimeUtil . LOCAL_FABRIC ) { const extensionData = GlobalState . get ( ) ; extensionData . deletedOneOrgLocalFabric = false ; await GlobalState . update ( extensionData ) ; } } if ( createMethod === UserInputUtil . ADD_ENVIRONMENT_FROM_MICROFAB ) { const extDir = vscode . workspace . getConfiguration ( ) . get ( SettingConfigurations . EXTENSION_DIRECTORY ) ; const resolvedExtDir = FileSystemUtil . getDirPath ( extDir ) ; envDir = path . join ( resolvedExtDir , FileConfigurations . FABRIC_ENVIRONMENTS , environmentName ) ; fabricEnvironmentEntry . environmentDirectory = envDir ; } if ( createMethod !== UserInputUtil . ADD_ENVIRONMENT_FROM_TEMPLATE ) { await fabricEnvironmentRegistry . add ( fabricEnvironmentEntry ) ; } if ( createMethod !== UserInputUtil . ADD_ENVIRONMENT_FROM_TEMPLATE && createMethod !== UserInputUtil . ADD_ENVIRONMENT_FROM_MICROFAB ) { let addedAllNodes ; if ( createMethod === UserInputUtil . ADD_ENVIRONMENT_FROM_OPS_TOOLS ) { addedAllNodes = await ExtensionUtil . executeCommandInternal ( ExtensionCommands . EDIT_NODE_FILTERS , fabricEnvironmentEntry , true , createMethod ) ; } else { addedAllNodes = await ExtensionUtil . executeCommandInternal ( ExtensionCommands . IMPORT_NODES_TO_ENVIRONMENT , fabricEnvironmentEntry , true , createMethod ) ; } if ( addedAllNodes === undefined ) { await fabricEnvironmentRegistry . delete ( fabricEnvironmentEntry . name ) ; return ; } else if ( addedAllNodes ) { const environment = EnvironmentFactory . getEnvironment ( fabricEnvironmentEntry ) ; const nodes = await environment . getNodes ( ) ; if ( nodes . length === 0 ) { outputAdapter . log ( LogType . SUCCESS , `Successfully added a new environment. No nodes included in current filters, click ${ fabricEnvironmentEntry . name } to edit filters` ) ; } else { outputAdapter . log ( LogType . SUCCESS , 'Successfully added a new environment' ) ; } } else { outputAdapter . log ( LogType . WARNING , 'Added a new environment, but some nodes could not be added' ) ; } } else { outputAdapter . log ( LogType . SUCCESS , 'Successfully added a new environment' ) ; } await vscode . commands . executeCommand ( ExtensionCommands . REFRESH_ENVIRONMENTS ) ; await vscode . commands . executeCommand ( ExtensionCommands . REFRESH_GATEWAYS ) ; await vscode . commands . executeCommand ( ExtensionCommands . REFRESH_WALLETS ) ; Reporter . instance ( ) . sendTelemetryEvent ( 'addEnvironmentCommand' ) ; } ) ; } catch ( error ) { if ( fabricEnvironmentEntry . name ) { if ( createMethod === UserInputUtil . ADD_ENVIRONMENT_FROM_TEMPLATE ) { const _settings = vscode . workspace . getConfiguration ( ) . get ( SettingConfigurations . FABRIC_RUNTIME , vscode . ConfigurationTarget . Global ) ; const localSettings = JSON . parse ( JSON . stringify ( _settings ) ) ; if ( localSettings [ fabricEnvironmentEntry . name ] ) { delete localSettings [ fabricEnvironmentEntry . name ] ; await vscode . workspace . getConfiguration ( ) . update ( SettingConfigurations . FABRIC_RUNTIME , localSettings , vscode . ConfigurationTarget . Global ) ; } try { await vscode . commands . executeCommand ( ExtensionCommands . TEARDOWN_FABRIC , undefined , true , fabricEnvironmentEntry . name ) ; } catch ( err ) { } } await fabricEnvironmentRegistry . delete ( fabricEnvironmentEntry . name , true ) ; if ( createMethod === UserInputUtil . ADD_ENVIRONMENT_FROM_TEMPLATE ) { LocalMicroEnvironmentManager . instance ( ) . removeRuntime ( fabricEnvironmentEntry . name ) ; } } outputAdapter . log ( LogType . ERROR , `Failed to add a new environment: ${ error . message }` , `Failed to add a new environment: ${ error . toString ( ) }` ) ; } async function getOpsToolsAccessInfo ( ) { const secureStore = await SecureStoreFactory . getSecureStore ( ) ; const HEALTH_CHECK = '/ak/api/v2/health' ; const GET_ALL_COMPONENTS = '/ak/api/v2/components' ; let url ; const userUrl = await UserInputUtil . showInputBox ( 'Enter the URL of the IBM Blockchain Platform Console you want to connect to' ) ; if ( ! userUrl ) { return ; } else { url = userUrl . split ( '/' , 3 ) . join ( '/' ) ; } const userAuth1 = await UserInputUtil . showInputBox ( 'Enter the API key or the User ID of the IBM Blockchain Platform Console you want to connect to' ) ; if ( ! userAuth1 ) { return ; } const userAuth2 = await UserInputUtil . showInputBox ( 'Enter the API secret or the password of the IBM Blockchain Platform Console you want to connect to' ) ; if ( ! userAuth2 ) { return ; } const healthUrl = url . replace ( /\/$/ , '' ) + HEALTH_CHECK ; const api = url . replace ( /\/$/ , '' ) + GET_ALL_COMPONENTS ; const requestOptions = { headers : { 'Content-Type' : 'application/json' } } ; requestOptions . httpsAgent = new https . Agent ( { rejectUnauthorized : true } ) ; try { await Axios . get ( healthUrl , requestOptions ) ; } catch ( errorWithAuth ) { if ( ! errorWithAuth . response || ! errorWithAuth . response . status || errorWithAuth . response . status !== 401 ) { try { requestOptions . httpsAgent . options . rejectUnauthorized = false ; await Axios . get ( healthUrl , requestOptions ) ; } catch ( errorWithoutAuth ) { if ( errorWithoutAuth . response && errorWithoutAuth . response . status && errorWithoutAuth . response . status === 401 ) { const options = [ { label : UserInputUtil . CONNECT_NO_CA_CERT_CHAIN , data : UserInputUtil . CONNECT_NO_CA_CERT_CHAIN } , { label : UserInputUtil . CANCEL_NO_CERT_CHAIN , data : UserInputUtil . CANCEL_NO_CERT_CHAIN , description : UserInputUtil . CANCEL_NO_CERT_CHAIN_DESCRIPTION } ] ; const certificateUsage = await UserInputUtil . showQuickPickItem ( 'Unable to perform certificate verification. Please choose how to proceed' , options ) as IBlockchainQuickPickItem < string > ; if ( ! certificateUsage || certificateUsage . label === UserInputUtil . CANCEL_NO_CERT_CHAIN ) { return ; } } else { throw new Error ( `Unable to connect to the IBM Blockchain Platform network: ${ errorWithoutAuth . message }` ) ; } } } } try { requestOptions . auth = { username : userAuth1 , password : userAuth2 } ; await Axios . get ( api , requestOptions ) ; } catch ( errorConnecting ) { throw new Error ( `Problem detected with the authentication information provided: ${ errorConnecting . message }` ) ; } try { await secureStore . setPassword ( 'blockchain-vscode-ext' , url , `${ userAuth1 }:${ userAuth2 }:${ requestOptions . httpsAgent . options . rejectUnauthorized }` ) ; } catch ( errorStorePass ) { throw new Error ( `Unable to store the required credentials: ${ errorStorePass . message }` ) ; } return url ; } async function getOpsToolsAccessInfoSaaS ( ) { const accessToken = await ExtensionsInteractionUtil . cloudAccountGetAccessToken ( ) ; if ( ! accessToken ) { return ; } const ibpResources = await ExtensionsInteractionUtil . cloudAccountGetIbpResources ( ) ; let chosenIbp ; if ( ibpResources . length === 0 ) { throw new Error ( 'There are no IBM Blockchain Platform service instances associated with the chosen account' ) ; } else { const ibpResourceItems = [ ] ; for ( const resource of ibpResources ) { ibpResourceItems . push ( { label : resource . name , description : resource . guid , data : resource } ) ; } if ( ibpResources . length === 1 ) { chosenIbp = ibpResourceItems [ 0 ] ; } else { chosenIbp = await UserInputUtil . showQuickPickItem ( 'Select an IBM Blockchain Platform service instance' , ibpResourceItems ) as IBlockchainQuickPickItem < any > ; if ( ! chosenIbp ) { return ; } } } const apiEndpoint = await ExtensionsInteractionUtil . cloudAccountGetApiEndpoint ( chosenIbp . data , accessToken ) ; return [ apiEndpoint , chosenIbp . label ] ; } }