import * as vscode from 'vscode' ; import * as path from 'path' ; import * as fs from 'fs-extra' ; import { YeomanUtil } from '../../util/YeomanUtil' ; import { FabricEnvironmentRegistry , FabricEnvironmentRegistryEntry , EnvironmentType , OutputAdapter , FileSystemUtil , FileConfigurations , LogType , MicrofabEnvironment , ConsoleOutputAdapter , FabricNode , FabricNodeType } from 'ibm-blockchain-platform-common' ; import { SettingConfigurations } from '../../configurations' ; import * as loghose from 'docker-loghose' ; import * as through from 'through2' ; import stripAnsi = require ( 'strip-ansi' ) ; import { FabricRuntimeState } from '../FabricRuntimeState' ; import { CommandUtil } from '../../util/CommandUtil' ; import { TimerUtil } from '../../util/TimerUtil' ; import { UserInputUtil } from '../../commands/UserInputUtil' ; export class LocalMicroEnvironment extends MicrofabEnvironment { public ourLoghose : any ; public port : number ; public numberOfOrgs : number ; public fabricCapabilities : string ; protected busy : boolean ; protected state : FabricRuntimeState ; protected isRunningPromise : Promise < boolean > ; protected lh : any ; private dockerName : string ; constructor ( name , port , numberOfOrgs , fabricCapabilities ) { const extDir = SettingConfigurations . getExtensionDir ( ) ; const resolvedExtDir = FileSystemUtil . getDirPath ( extDir ) ; const envPath = path . join ( resolvedExtDir , FileConfigurations . FABRIC_ENVIRONMENTS , name ) ; const url = `http://console.127-0-0-1.nip.io:${ port }` ; super ( name , envPath , url ) ; const dockerName = name . replace ( /[^A-Za-z0-9]/g , '' ) ; this . dockerName = dockerName ; this . port = port ; this . numberOfOrgs = numberOfOrgs ; this . fabricCapabilities = fabricCapabilities ; } public async create ( ) { await FabricEnvironmentRegistry . instance ( ) . delete ( this . name , true ) ; const settings = vscode . workspace . getConfiguration ( ) . get ( SettingConfigurations . FABRIC_RUNTIME , vscode . ConfigurationTarget . Global ) ; const port = settings [ this . name ] ; if ( port ) { if ( ! this . port || ( port !== this . port ) ) { this . port = port ; } } this . url = `console.127-0-0-1.nip.io:${ this . port }` ; this . setClient ( this . url ) ; if ( ! this . fabricCapabilities ) { this . fabricCapabilities = UserInputUtil . V2_0 ; } const registryEntry = new FabricEnvironmentRegistryEntry ( { name : this . name , managedRuntime : true , environmentType : EnvironmentType . LOCAL_MICROFAB_ENVIRONMENT , environmentDirectory : path . join ( this . path ) , numberOfOrgs : this . numberOfOrgs , url : this . url , fabricCapabilities : this . fabricCapabilities } ) ; await FabricEnvironmentRegistry . instance ( ) . add ( registryEntry ) ; await YeomanUtil . run ( 'fabric:network' , { destination : this . path , name : this . name , dockerName : this . dockerName , numOrganizations : this . numberOfOrgs , port : this . port , fabricCapabilities : this . fabricCapabilities } ) ; } public async start ( outputAdapter ? ) { try { const created = await this . isCreated ( ) ; if ( ! created ) { await this . create ( ) ; } this . setBusy ( true ) ; this . setState ( FabricRuntimeState . STARTING ) ; await this . startInner ( outputAdapter ) ; } finally { this . setBusy ( false ) ; const running = await this . isRunning ( ) ; if ( running ) { this . setState ( FabricRuntimeState . STARTED ) ; } else { this . setState ( FabricRuntimeState . STOPPED ) ; } } } public async stop ( outputAdapter ? ) { try { this . setBusy ( true ) ; this . setState ( FabricRuntimeState . STOPPING ) ; await this . stopInner ( outputAdapter ) ; } finally { this . setBusy ( false ) ; const running = await this . isRunning ( ) ; if ( running ) { this . setState ( FabricRuntimeState . STARTED ) ; } else { this . setState ( FabricRuntimeState . STOPPED ) ; } } } public async restart ( outputAdapter ? ) { try { this . setBusy ( true ) ; this . setState ( FabricRuntimeState . RESTARTING ) ; await this . stopInner ( outputAdapter ) ; await this . startInner ( outputAdapter ) ; } finally { this . setBusy ( false ) ; const running = await this . isRunning ( ) ; if ( running ) { this . setState ( FabricRuntimeState . STARTED ) ; } else { this . setState ( FabricRuntimeState . STOPPED ) ; } } } param attempts { number } param interval { number } returns { Promise < boolean > } public async waitFor ( attempts = 10 , interval = 2 ) { let isAlive = await this . isAlive ( ) ; if ( ! isAlive ) { for ( let currentAttempt = 1 ; currentAttempt <= attempts ; currentAttempt ++ ) { isAlive = await this . isAlive ( ) ; if ( isAlive ) { break ; } else { await TimerUtil . sleep ( interval * 1000 ) ; } } } return isAlive ; } public getState ( ) { return this . state ; } public isBusy ( ) { return this . busy ; } public async isCreated ( ) { let entry ; try { entry = await FabricEnvironmentRegistry . instance ( ) . get ( this . name ) ; } catch ( err ) { return false ; } const startFilePath = path . join ( entry . environmentDirectory , 'start.sh' ) ; const startFileExists = await fs . pathExists ( startFilePath ) ; return startFileExists ; } public async teardown ( outputAdapter ? ) { try { await this . teardownInner ( outputAdapter ) ; await this . create ( ) ; } finally { await this . setTeardownState ( ) ; } } public async delete ( outputAdapter ? ) { try { await this . teardownInner ( outputAdapter ) ; } finally { await this . setTeardownState ( ) ; } } public async updateUserSettings ( name ) { const _settings = vscode . workspace . getConfiguration ( ) . get ( SettingConfigurations . FABRIC_RUNTIME , vscode . ConfigurationTarget . Global ) ; const settings = JSON . parse ( JSON . stringify ( _settings ) ) ; settings [ name ] = this . port ; await vscode . workspace . getConfiguration ( ) . update ( SettingConfigurations . FABRIC_RUNTIME , settings , vscode . ConfigurationTarget . Global ) ; } public async killChaincode ( args ? , outputAdapter ? ) { await this . killChaincodeInner ( args , outputAdapter ) ; } public startLogs ( outputAdapter ) { const opts = { newline : true } ; const lh = this . getLoghose ( opts ) ; lh . pipe ( through . obj ( ( chunk , _enc , cb ) => { const name = chunk . name ; const line = stripAnsi ( chunk . line ) ; outputAdapter . log ( LogType . INFO , undefined , `${ name }|${ line }` ) ; cb ( ) ; } ) ) ; this . lh = lh ; } public stopLogs ( ) { if ( this . lh ) { this . lh . destroy ( ) ; } this . lh = null ; } public getLoghose ( opts ) { return loghose ( opts ) ; } public setBusy ( busy ) { this . busy = busy ; this . emit ( 'busy' , busy ) ; } public setState ( state ) { this . state = state ; } public async execute ( script , args = [ ] , outputAdapter ? ) { if ( ! outputAdapter ) { outputAdapter = ConsoleOutputAdapter . instance ( ) ; } const env = Object . assign ( { } , process . env , { CORE_CHAINCODE_MODE : 'dev' } ) ; if ( process . platform === 'win32' ) { await CommandUtil . sendCommandWithOutput ( 'cmd' , [ '/c' , `${ script }.cmd` , ... args ] , this . path , env , outputAdapter ) ; } else { await CommandUtil . sendCommandWithOutput ( '/bin/sh' , [ `${ script }.sh` , ... args ] , this . path , env , outputAdapter ) ; } } public isRunning ( args ? ) { if ( this . isRunningPromise ) { return this . isRunningPromise ; } this . isRunningPromise = this . isRunningInner ( args ) . then ( ( result ) => { this . isRunningPromise = undefined ; return result ; } ) ; return this . isRunningPromise ; } public async getPeerChaincodeURL ( orgName ? ) { let nodes = await this . getNodes ( ) ; if ( orgName ) { nodes = nodes . filter ( ( node ) => { return node . wallet === orgName ; } ) ; } const peer = nodes . find ( ( node ) => node . type === FabricNodeType . PEER ) ; if ( ! peer ) { throw new Error ( 'There are no Fabric peer nodes' ) ; } return peer . chaincode_url ; } protected async setTeardownState ( ) { this . setBusy ( false ) ; const running = await this . isRunning ( ) ; if ( running ) { this . setState ( FabricRuntimeState . STARTED ) ; } else { this . setState ( FabricRuntimeState . STOPPED ) ; } } protected async isRunningInner ( args ? ) { const created = await this . isCreated ( ) ; if ( ! created ) { return false ; } try { await this . execute ( 'is_running' , args ) ; return true ; } catch ( error ) { return false ; } } protected async teardownInner ( outputAdapter ? ) { this . setBusy ( true ) ; this . setState ( FabricRuntimeState . STOPPING ) ; this . stopLogs ( ) ; await this . execute ( 'teardown' , [ ] , outputAdapter ) ; } protected async stopInner ( outputAdapter ? ) { this . stopLogs ( ) ; await this . execute ( 'stop' , [ ] , outputAdapter ) ; } private async startInner ( outputAdapter ? ) { const startArgs = [ ] ; const enableCustomImage = vscode . workspace . getConfiguration ( ) . get ( SettingConfigurations . EXTENSION_ENABLE_CUSTOM_LOCAL_ENVIRONMENT_START_IMAGE ) ; if ( enableCustomImage ) { const image = vscode . workspace . getConfiguration ( ) . get ( SettingConfigurations . EXTENSION_CUSTOM_LOCAL_ENVIRONMENT_START_IMAGE_VALUE ) ; startArgs . push ( image ) ; } await this . execute ( 'start' , startArgs , outputAdapter ) ; } private async killChaincodeInner ( args , outputAdapter ? ) { await this . execute ( 'kill_chaincode' , args , outputAdapter ) ; } }