@milaboratories/pl-deployments 1.1.16 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2 -2
- package/dist/index.mjs.map +1 -1
- package/package.json +4 -4
- package/src/local/pl.ts +1 -1
package/dist/index.js
CHANGED
|
@@ -4,7 +4,7 @@ wd: ${r.opts.cwd}`),s.info(" spawning child process"),it.spawn(r.cmd,r.args,r.o
|
|
|
4
4
|
URL: ${r}
|
|
5
5
|
Save to: ${t}`);const{body:i,statusCode:o}=await ot.request(r);if(e.statusCode=o,o!=200){const n=await st.text(i);throw e.errorMsg=`failed to download archive: ${o}, response: ${n.slice(0,1e3)}`,s.error(e.errorMsg),new Error(e.errorMsg)}return e.tmpPath=t+".tmp",await U.Readable.toWeb(i).pipeTo(U.Writable.toWeb(D.createWriteStream(e.tmpPath))),e.wroteTmp=!0,e.tmpExisted=await w.fileExists(e.tmpPath),await m.rename(e.tmpPath,t),e.renamed=!0,e.newExisted=await w.fileExists(t),e}catch(i){const o=`downloadArchive: error ${JSON.stringify(i)} occurred, state: ${JSON.stringify(e)}`;throw s.error(o),new Error(o)}}const yt=".ok";async function $t(s,r,t,e){if(s.info("extracting archive..."),s.info(` archive path: '${r}'`),s.info(` target dir: '${e}'`),!await w.fileExists(r)){const o=`Platforma Backend binary archive not found at '${r}'`;throw s.error(o),new Error(o)}const i=h.join(e,yt);if(await w.fileExists(i)){s.info(`Platforma Backend binaries unpack skipped: '${e}' exists`);return}switch(await w.fileExists(e)&&(s.info(`Removing previous incompletely unpacked folder: '${e}'`),await m.rm(e,{recursive:!0})),s.info(` creating target dir '${e}'`),await m.mkdir(e,{recursive:!0}),s.info(`Unpacking Platforma Backend archive:
|
|
6
6
|
Archive: ${r}
|
|
7
|
-
Target dir: ${e}`),t){case"tgz":await ut.x({file:r,cwd:e,gzip:!0});break;case"zip":await at(r,e);break;default:w.assertNever(t)}await m.writeFile(i,"ok"),s.info(" ... unpack done.")}const Pt={linux:"tgz",macos:"tgz",windows:"zip"};function E(){return"1.
|
|
7
|
+
Target dir: ${e}`),t){case"tgz":await ut.x({file:r,cwd:e,gzip:!0});break;case"zip":await at(r,e);break;default:w.assertNever(t)}await m.writeFile(i,"ok"),s.info(" ... unpack done.")}const Pt={linux:"tgz",macos:"tgz",windows:"zip"};function E(){return"1.23.0"}function vt(){return{type:"Download",version:E()}}async function St(s,r,t){switch(t.type){case"Download":const e=await gt(s,r,"pl",`pl-${t.version}`,F.arch(),F.platform());return h.join(e.baseName,"binaries",Ct[k(F.platform())]);case"Local":return t.path;default:w.assertNever(t)}}const Ct={linux:"platforma",macos:"platforma",windows:"platforma.exe"};function M(s){return h.join(s,"pl_pid")}async function Et(s){if(!await w.fileExists(s))return;const r=await m.readFile(s);return Number(r.toString())}async function At(s,r){await m.writeFile(s,JSON.stringify(r))}function bt(){return{}}function Ft(s,r,t){return s[r]=t,t}async function R(s,r){const t=bt();try{return await r((i,o)=>Ft(t,i,o),t)}catch(e){throw s.error(`error ${e} while doing traced operation, state: ${JSON.stringify(t)}`),e}}const J="config-local.yaml";class L{constructor(r,t,e,i,o,n,c,a){f(this,"instance");f(this,"pid");f(this,"nRuns",0);f(this,"lastRunHistory",{});f(this,"wasStopped",!1);this.logger=r,this.workingDir=t,this.startOptions=e,this.initialStartHistory=i,this.onClose=o,this.onError=n,this.onCloseAndError=c,this.onCloseAndErrorNoStop=a}async start(){await R(this.logger,async(r,t)=>{this.wasStopped=!1;const e=ht(this.logger,this.startOptions);e.on("error",o=>{this.logger.error(`error '${o}', while running platforma, started opts: ${JSON.stringify(this.debugInfo())}`),this.onError!==void 0&&this.onError(this),this.onCloseAndError!==void 0&&this.onCloseAndError(this),this.onCloseAndErrorNoStop!==void 0&&!this.wasStopped&&this.onCloseAndErrorNoStop(this)}),e.on("close",()=>{this.logger.warn(`platforma was closed, started opts: ${JSON.stringify(this.debugInfo())}`),this.onClose!==void 0&&this.onClose(this),this.onCloseAndError!==void 0&&this.onCloseAndError(this),this.onCloseAndErrorNoStop!==void 0&&!this.wasStopped&&this.onCloseAndErrorNoStop(this)}),r("started",!0);const i=r("pidFile",M(this.workingDir));r("pid",w.notEmpty(e.pid)),r("pidWritten",await At(i,w.notEmpty(e.pid))),this.nRuns++,this.instance=e,this.pid=e.pid,this.lastRunHistory=t})}stop(){this.wasStopped=!0,H(w.notEmpty(this.pid))}async waitStopped(){await q(w.notEmpty(this.pid),15e3)}stopped(){return this.wasStopped}async isAlive(){return await x(w.notEmpty(this.pid))}debugInfo(){return{lastRunHistory:this.lastRunHistory,nRuns:this.nRuns,pid:this.pid,workingDir:this.workingDir,initialStartHistory:this.initialStartHistory,wasStopped:this.wasStopped}}}async function Dt(s,r){const t={plBinary:vt(),spawnOptions:{},closeOld:!0,...r};return await R(s,async(e,i)=>{e("startOptions",{...t,config:"too wordy"});const o=h.resolve(t.workingDir);t.closeOld&&e("closeOld",await Ot(s,o));const n=h.join(o,J);s.info(`writing configuration '${n}'...`),await m.writeFile(n,t.config);const c=h.join(o,"binaries"),a=await St(s,c,t.plBinary),u={cmd:e("binaryPath",h.join("binaries",a)),args:["--config",n],opts:{env:{...process.env},cwd:o,stdio:["pipe","ignore","inherit"],windowsHide:!0,...t.spawnOptions}};e("processOpts",{cmd:u.cmd,args:u.args,cwd:u.opts.cwd});const d=new L(s,t.workingDir,u,i,t.onClose,t.onError,t.onCloseAndError,t.onCloseAndErrorNoStop);return await d.start(),d})}async function Ot(s,r){return await R(s,async(t,e)=>{const i=t("pidFilePath",M(r)),o=t("pid",await Et(i)),n=t("wasAlive",await x(o));return o!==void 0&&n&&(t("stopped",H(o)),t("waitStopped",await q(o,1e4))),e})}const xt={keepaliveInterval:6e4,keepaliveCountMax:10};class A{constructor(r,t){f(this,"config");f(this,"homeDir");f(this,"forwardedServers",[]);this.logger=r,this.client=t}static async init(r,t){const e={...xt,...t},i=new A(r,new S.Client);return await i.connect(e),i}getForwardedServers(){return this.forwardedServers}getFullHostName(){var r,t;return`${(r=this.config)==null?void 0:r.host}:${(t=this.config)==null?void 0:t.port}`}getUserName(){var r;return(r=this.config)==null?void 0:r.username}async connect(r){return this.config=r,await kt(this.client,r)}async exec(r){return new Promise((t,e)=>{this.client.exec(r,(i,o)=>{if(i)return e(`ssh.exec: ${r}, error occurred: ${i}`);let n="",c="";o.on("close",a=>{a===0?t({stdout:n,stderr:c}):e(new Error(`Command ${r} exited with code ${a}`))}).on("data",a=>{n+=a.toString()}).stderr.on("data",a=>{c+=a.toString()})})})}static async getAuthTypes(r,t){return new Promise(e=>{let i="";const o=new S.Client;o.on("ready",()=>{o.end();const n=this.extractAuthMethods(i);e(n.length===0?["publickey","password"]:n)}),o.on("error",()=>{o.end(),e(["publickey","password"])}),o.connect({host:r,port:t,username:new Date().getTime().toString(),debug:n=>{i+=`${n}
|
|
8
8
|
`}})})}static extractAuthMethods(r){const t=r.match(/Inbound: Received USERAUTH_FAILURE \((.+)\)/);return t&&t[1]?t[1].split(",").map(e=>e.trim()):[]}async forwardPort(r,t){const e=`ssh.forward:${r.localPort}:${r.remotePort}.id_${O.randomBytes(1).toString("hex")}`;t=t??this.config;const i=new w.RetryablePromise(o=>new Promise((n,c)=>{const a=new S.Client;a.on("ready",()=>{this.logger.info(`${e}.client.ready`),n(a)}),a.on("error",l=>{this.logger.info(`${e}.client.error: ${l}`),o.reset(),c(l)}),a.on("close",()=>{this.logger.info(`${e}.client.closed`),o.reset()}),a.connect(t)}));return await i.ensure(),new Promise((o,n)=>{const c=j.createServer({pauseOnConnect:!0},async a=>{const l=`${e}.sock_${O.randomBytes(1).toString("hex")}`;let u;try{u=await i.ensure()}catch(y){this.logger.info(`${l}.persistentClient.catch: ${y}`),a.end();return}let d;try{d=await Rt(this.logger,u,"127.0.0.1",0,"127.0.0.1",r.remotePort)}catch(y){this.logger.error(`${l}.forwardOut.err: ${y}`),a.end();return}a.pipe(d),d.pipe(a),a.resume(),d.on("error",y=>{this.logger.error(`${l}.stream.error: ${y}`),a.end(),d.end()}),d.on("close",()=>{a.end(),d.end()}),a.on("close",()=>{this.logger.info(`${l}.localSocket: closed`),a.end(),d.end()})});c.listen(r.localPort,"127.0.0.1",()=>{this.logger.info(`${e}.server: started listening`),this.forwardedServers.push(c),o({server:c})}),c.on("error",a=>{c.close(),n(new Error(`${e}.server: error: ${JSON.stringify(a)}`))}),c.on("close",()=>{this.logger.info(`${e}.server: closed ${JSON.stringify(r)}`),this.forwardedServers=this.forwardedServers.filter(a=>a!==c)})})}closeForwardedPorts(){this.logger.info("[SSH] Closing all forwarded ports..."),this.forwardedServers.forEach(r=>{const t=r.address();if(t&&typeof t!="string"){const e=t;this.logger.info(`[SSH] Closing port forward for server ${e.address}:${e.port}`)}r.close()}),this.forwardedServers=[]}static async checkHostAvailability(r){return new Promise(t=>{ct.lookup(r,e=>{t(!e)})})}static async isPassphraseRequiredForKey(r){return new Promise((t,e)=>{try{return S.utils.parseKey(r)instanceof Error&&t(!0),t(!1)}catch(i){console.log("Error parsing privateKey"),e(new Error(`ssh.isPassphraseRequiredForKey: err ${i}`))}})}async uploadFile(r,t){return await this.withSftp(async e=>new Promise((i,o)=>{e.fastPut(r,t,n=>{if(n){const c=new Error(`ssh.uploadFile: err: ${n}, localPath: ${r}, remotePath: ${t}`);return o(c)}i(!0)})}))}async withSftp(r){return new Promise((t,e)=>{this.client.sftp((i,o)=>{if(i)return e(new Error(`ssh.withSftp: sftp err: ${i}`));r(o).then(t).catch(n=>{e(new Error(`ssh.withSftp.callback: err ${n}`))}).finally(()=>{o==null||o.end()})})})}async writeFileOnTheServer(r,t,e=432){return this.withSftp(async i=>this.writeFile(i,r,t,e))}async getForderStructure(r,t,e={files:[],directories:[]}){return new Promise((i,o)=>{r.readdir(t,async(n,c)=>{if(n)return o(n);for(const a of c){const l=`${t}/${a.filename}`;if(a.attrs.isDirectory()){e.directories.push(l);try{await this.getForderStructure(r,l,e)}catch(u){return o(u)}}else e.files.push(l)}i(e)})})}rmdir(r,t){return new Promise((e,i)=>{r.rmdir(t,o=>o?i(o):e(!0))})}unlink(r,t){return new Promise((e,i)=>{r.unlink(t,o=>o?i(o):e(!0))})}async deleteFolder(r){return this.withSftp(async t=>{try{const e=await this.getForderStructure(t,r);this.logger.info("ssh.deleteFolder list of files and directories"),this.logger.info(`ssh.deleteFolder list of files: ${e.files}`),this.logger.info(`ssh.deleteFolder list of directories: ${e.directories}`);for(const i of e.files)this.logger.info(`ssh.deleteFolder unlink file ${i}`),await this.unlink(t,i);e.directories.sort((i,o)=>o.length-i.length);for(const i of e.directories)this.logger.info(`ssh.deleteFolder rmdir ${i}`),await this.rmdir(t,i);return await this.rmdir(t,r),!0}catch(e){this.logger.error(e);const i=e instanceof Error?e.message:"";throw new Error(`ssh.deleteFolder: path: ${r}, message: ${i}`)}})}async readFile(r){return this.withSftp(async t=>new Promise((e,i)=>{t.readFile(r,(o,n)=>{if(o)return i(new Error(`ssh.readFile: err occurred ${o}`));e(n.toString())})}))}async chmod(r,t){return this.withSftp(async e=>new Promise((i,o)=>{e.chmod(r,t,n=>n?o(new Error(`ssh.chmod: ${n}, path: ${r}, mode: ${t}`)):i(void 0))}))}async checkFileExists(r){return this.withSftp(async t=>new Promise((e,i)=>{t.stat(r,(o,n)=>{if(o)return(o==null?void 0:o.code)===2?e(!1):i(new Error(`ssh.checkFileExists: err ${o}`));e(n.isFile())})}))}async checkPathExists(r){return this.withSftp(async t=>new Promise((e,i)=>{t.stat(r,(o,n)=>{if(o)return o.code===2?e({exists:!1,isFile:!1,isDirectory:!1}):i(new Error(`ssh.checkPathExists: ${o}`));e({exists:!0,isFile:n.isFile(),isDirectory:n.isDirectory()})})}))}async writeFile(r,t,e,i=432){return new Promise((o,n)=>{r.writeFile(t,e,{mode:i},c=>{if(c)return n(new Error(`ssh.writeFile: err ${c}, remotePath: ${t}`));o(!0)})})}uploadFileUsingExistingSftp(r,t,e,i=432){return new Promise((o,n)=>{m.readFile(t).then(async c=>{this.writeFile(r,e,c,i).then(()=>{o(void 0)}).catch(a=>{const l=`uploadFileUsingExistingSftp: error ${a} occurred`;this.logger.error(l),n(new Error(l))})})})}async __uploadDirectory(r,t,e,i=432){return new Promise((o,n)=>{D.readdir(t,async(c,a)=>{if(c)return n(new Error(`ssh.__uploadDir: err ${c}, localDir: ${t}, remoteDir: ${e}`));try{await this.__createRemoteDirectory(r,e);for(const l of a){const u=h.join(t,l),d=`${e}/${l}`;D.lstatSync(u).isDirectory()?await this.__uploadDirectory(r,u,d,i):await this.uploadFileUsingExistingSftp(r,u,d,i)}o()}catch(l){const u=`ssh.__uploadDir: catched err ${l}`;this.logger.error(u),n(new Error(u))}})})}async uploadDirectory(r,t,e=432){return new Promise((i,o)=>{this.withSftp(async n=>{try{await this.__uploadDirectory(n,r,t,e),i()}catch(c){o(new Error(`ssh.uploadDirectory: ${c}`))}})})}__createRemoteDirectory(r,t){return new Promise((e,i)=>{const o=t.split("/");let n="";const c=a=>{if(a>=o.length)return e();n+=`${o[a]}/`,r.stat(n,l=>{l?r.mkdir(n,u=>{if(u)return i(new Error(`ssh.__createRemDir: err ${u}, remotePath: ${t}`));c(a+1)}):c(a+1)})};c(0)})}ensureRemoteDirCreated(r,t=493){return this.withSftp(async e=>{const i=r.split("/");let o="";for(const n of i){o+=`${n}/`;try{await new Promise((c,a)=>{e.stat(o,l=>{if(!l)return c();e.mkdir(o,{mode:t},u=>{if(u)return a(new Error(`ssh.createRemoteDir: err ${u}, remotePath: ${r}`));c()})})})}catch(c){throw console.error(`Failed to create directory: ${o}`,c),c}}})}async downloadFile(r,t){return this.withSftp(async e=>new Promise((i,o)=>{e.fastGet(r,t,n=>{if(n)return o(new Error(`ssh.downloadFile: err ${n}, remotePath: ${r}, localPath: ${t}`));i(!0)})}))}close(){this.closeForwardedPorts(),this.client.end()}}async function kt(s,r,t,e){return new Promise((i,o)=>{s.on("ready",()=>{i(s)}),s.on("error",n=>{o(new Error(`ssh.connect: error occurred: ${n}`))}),s.on("close",()=>{}),s.connect(r)})}async function Rt(s,r,t,e,i,o){return new Promise((n,c)=>{r.forwardOut(t,e,i,o,(a,l)=>a?(s.error(`forwardOut.error: ${a}`),c(a)):n(l))})}const Bt="minio-2024-12-18T13-15-44Z",Nt="supervisord-0.7.3",Ut="supervisord_0.7.3_Linux_64-bit";function g(s){return h.join(s,".platforma_ssh")}function b(s){return h.join(g(s),"binaries")}function It(s,r){return h.join(b(s),`pl-${E()}-${v(r)}`)}function G(s,r){return h.join(It(s,r),"binaries")}function I(s,r){return h.join(G(s,r),"platforma")}function _t(s,r){return h.join(G(s,r),"free-port")}function W(s,r){return h.join(b(s),`minio-2024-12-18T13-15-44Z-${v(r)}`)}function Tt(s,r){return h.join(W(s,r),"minio")}function jt(s,r){return h.join(b(s),`supervisord-0.7.3-${v(r)}`,Ut)}function V(s,r){return h.join(jt(s,r),"supervisord")}function K(s){return h.join(g(s),"supervisor.conf")}function _(s){return h.join(g(s),"connection.txt")}async function Ht(s,r,t){const e=await B(s,r,t,"--daemon");if(e.stderr)throw new Error(`Can not run ssh Platforma ${e.stderr}`)}async function qt(s,r,t){const e=await B(s,r,t,"ctl shutdown");if(e.stderr)throw new Error(`Can not stop ssh Platforma ${e.stderr}`)}async function zt(s,r,t,e){let i;try{i=await B(r,t,e,"ctl status")}catch(a){return{execError:String(a),allAlive:!1}}if(i.stderr)return s.info(`supervisord ctl status: stderr occurred: ${i.stderr}, stdout: ${i.stdout}`),{rawResult:i,allAlive:!1};const o=T(i.stdout,"platforma"),n=T(i.stdout,"minio"),c={rawResult:i,platforma:o,minio:n,allAlive:o&&n};return c.allAlive||(c.minio||s.warn("Minio is not running on the server"),c.platforma||s.warn("Platforma is not running on the server")),c}function Mt(s,r,t,e,i,o,n){const c=Object.entries(r).map(([u,d])=>`${u}="${d}"`).join(","),a=O.randomBytes(16).toString("hex"),l=t;return`
|
|
9
9
|
[supervisord]
|
|
10
10
|
logfile=${e}/supervisord.log
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../src/local/process.ts","../src/common/os_and_arch.ts","../src/common/pl_binary_download.ts","../src/common/pl_version.ts","../src/common/pl_binary.ts","../src/local/pid.ts","../src/local/trace.ts","../src/local/pl.ts","../src/ssh/ssh.ts","../src/ssh/pl_paths.ts","../src/ssh/supervisord.ts","../src/ssh/connection_info.ts","../src/ssh/pl.ts"],"sourcesContent":["import type { SpawnOptions, ChildProcess } from 'child_process';\nimport { spawn } from 'child_process';\nimport type { MiLogger } from '@milaboratories/ts-helpers';\nimport { sleep } from '@milaboratories/ts-helpers';\n\nexport type ProcessOptions = {\n cmd: string;\n args: string[];\n opts: SpawnOptions;\n};\n\nexport function processRun(logger: MiLogger, opts: ProcessOptions): ChildProcess {\n logger.info(`Running:\ncmd: ${JSON.stringify([opts.cmd, ...opts.args])}\nwd: ${opts.opts.cwd}`);\n\n logger.info(' spawning child process');\n return spawn(opts.cmd, opts.args, opts.opts);\n}\n\nexport async function isProcessAlive(pid: number) {\n try {\n process.kill(pid, 0);\n return true;\n } catch (e) {\n return false;\n }\n}\n\nexport function processStop(pid: number) {\n return process.kill(pid, 'SIGINT');\n}\n\nexport async function processWaitStopped(pid: number, maxMs: number) {\n const sleepMs = 100;\n let total = 0;\n while (await isProcessAlive(pid)) {\n await sleep(sleepMs);\n total += sleepMs;\n if (total > maxMs) {\n throw new Error(`The process did not stopped after ${maxMs} ms.`);\n }\n }\n}\n","import os from 'os';\n\nexport const OSes = ['linux', 'macos', 'windows'] as const;\nexport type OSType = (typeof OSes)[number];\n\n/** @param osName - should be the thing returned from either {@link os.platform())} or `uname -s` */\nexport function newOs(osName: string): OSType {\n switch (osName.toLowerCase()) {\n case 'darwin':\n return 'macos';\n case 'linux':\n return 'linux';\n case 'win32':\n return 'windows';\n default:\n throw new Error(\n `operating system '${osName}' is not currently supported by Platforma ecosystem. The list of OSes supported: `\n + JSON.stringify(OSes),\n );\n }\n}\n\nexport const Arches = ['amd64', 'arm64'] as const;\nexport type ArchType = (typeof Arches)[number];\n\n/** @param arch - should be the thing returned from either {@link os.arch())} or `uname -m` */\nexport function newArch(arch: string): ArchType {\n switch (arch) {\n case 'aarch64':\n case 'aarch64_be':\n case 'arm64':\n return 'arm64';\n\n case 'x86_64':\n case 'x64':\n return 'amd64';\n\n default:\n throw new Error(\n `processor architecture '${arch}' is not currently supported by Platforma ecosystem. The list of architectures supported: `\n + JSON.stringify(Arches),\n );\n }\n}\n","import fs from 'fs';\nimport fsp from 'fs/promises';\nimport upath from 'upath';\nimport { request } from 'undici';\nimport { Writable, Readable } from 'stream';\nimport { text } from 'stream/consumers';\nimport * as tar from 'tar';\nimport type { MiLogger } from '@milaboratories/ts-helpers';\nimport { assertNever, fileExists } from '@milaboratories/ts-helpers';\nimport decompress from 'decompress';\nimport type { ArchType, OSType } from './os_and_arch';\nimport { newOs, newArch } from './os_and_arch';\n\nconst cdn = 'https://cdn.platforma.bio/software';\n// We'll download things from Global Access if downloading from CDN has failed\n// (it might be that it's blocked from the company's network.)\nconst gaCdn = 'https://cdn-ga.pl-open.science/software';\n\nexport type DownloadBinaryResult = {\n archiveUrl: string;\n alternativeArchiveGAUrl: string;\n wasDownloadedFrom?: string;\n archivePath: string;\n archiveType: ArchiveType;\n targetFolder: string;\n baseName: string;\n};\n\nexport async function downloadBinaryNoExtract(\n logger: MiLogger,\n baseDir: string,\n softwareName: string,\n tgzName: string,\n arch: string,\n platform: string,\n): Promise<DownloadBinaryResult> {\n const opts = getPathsForDownload(softwareName, tgzName, baseDir, newArch(arch), newOs(platform));\n const { archiveUrl, alternativeArchiveGAUrl, archivePath } = opts;\n\n try {\n await downloadArchive(logger, archiveUrl, archivePath);\n opts.wasDownloadedFrom = archiveUrl;\n } catch (e: unknown) {\n await downloadArchive(logger, alternativeArchiveGAUrl, archivePath);\n opts.wasDownloadedFrom = alternativeArchiveGAUrl;\n }\n\n return opts;\n}\n\nexport async function downloadBinary(\n logger: MiLogger,\n baseDir: string,\n softwareName: string,\n archiveName: string,\n arch: string,\n platform: string,\n): Promise<DownloadBinaryResult> {\n const opts = getPathsForDownload(softwareName, archiveName, baseDir, newArch(arch), newOs(platform));\n const { archiveUrl, alternativeArchiveGAUrl, archivePath, archiveType, targetFolder, baseName } = opts;\n\n try {\n await downloadArchive(logger, archiveUrl, archivePath);\n opts.wasDownloadedFrom = archiveUrl;\n } catch (e: unknown) {\n await downloadArchive(logger, alternativeArchiveGAUrl, archivePath);\n opts.wasDownloadedFrom = alternativeArchiveGAUrl;\n }\n\n await extractArchive(logger, archivePath, archiveType, targetFolder);\n\n return opts;\n}\n\nfunction getPathsForDownload(\n softwareName: string,\n archiveName: string,\n baseDir: string,\n arch: ArchType,\n os: OSType,\n): DownloadBinaryResult {\n const baseName = `${archiveName}-${arch}`;\n const archiveType = osToArchiveType[os];\n\n const archiveFileName = `${baseName}.${archiveType}`;\n const archiveUrl = `${cdn}/${softwareName}/${os}/${archiveFileName}`;\n const alternativeArchiveGAUrl = `${gaCdn}/${softwareName}/${os}/${archiveFileName}`;\n const archivePath = upath.join(baseDir, archiveFileName);\n // folder where binary distribution of pl will be unpacked\n const targetFolder = upath.join(baseDir, baseName);\n\n return {\n archiveUrl,\n alternativeArchiveGAUrl,\n archivePath,\n archiveType,\n targetFolder,\n baseName,\n };\n}\n\nexport type DownloadInfo = {\n dstArchive?: string;\n fileExisted?: boolean;\n dirnameCreated?: boolean;\n statusCode?: number;\n errorMsg?: string;\n tmpPath?: string;\n wroteTmp?: boolean;\n tmpExisted?: boolean;\n renamed?: boolean;\n newExisted?: boolean;\n};\n\nexport async function downloadArchive(\n logger: MiLogger, archiveUrl: string, dstArchiveFile: string,\n): Promise<DownloadInfo> {\n const state: DownloadInfo = {};\n state.dstArchive = dstArchiveFile;\n\n try {\n state.fileExisted = await fileExists(dstArchiveFile);\n if (state.fileExisted) {\n logger.info(`Platforma Backend archive download skipped: '${dstArchiveFile}' already exists`);\n return state;\n }\n\n await fsp.mkdir(upath.dirname(dstArchiveFile), { recursive: true });\n state.dirnameCreated = true;\n\n logger.info(`Downloading archive:\\n URL: ${archiveUrl}\\n Save to: ${dstArchiveFile}`);\n\n const { body, statusCode } = await request(archiveUrl);\n state.statusCode = statusCode;\n if (statusCode != 200) {\n // completely draining the stream to prevent leaving open connections\n const textBody = await text(body);\n state.errorMsg = `failed to download archive: ${statusCode}, response: ${textBody.slice(0, 1000)}`;\n logger.error(state.errorMsg);\n throw new Error(state.errorMsg);\n }\n\n // to prevent incomplete downloads we first write in a temp file\n state.tmpPath = dstArchiveFile + '.tmp';\n await Readable.toWeb(body).pipeTo(Writable.toWeb(fs.createWriteStream(state.tmpPath)));\n state.wroteTmp = true;\n state.tmpExisted = await fileExists(state.tmpPath);\n\n // and then atomically rename it\n await fsp.rename(state.tmpPath, dstArchiveFile);\n state.renamed = true;\n state.newExisted = await fileExists(dstArchiveFile);\n\n return state;\n } catch (e: unknown) {\n const msg = `downloadArchive: error ${JSON.stringify(e)} occurred, state: ${JSON.stringify(state)}`;\n logger.error(msg);\n throw new Error(msg);\n }\n}\n\n/** Used to prevent mid-way interrupted unarchived folders to be used */\nconst MarkerFileName = '.ok';\n\nexport async function extractArchive(\n logger: MiLogger,\n archivePath: string,\n archiveType: ArchiveType,\n dstFolder: string,\n) {\n logger.info('extracting archive...');\n logger.info(` archive path: '${archivePath}'`);\n logger.info(` target dir: '${dstFolder}'`);\n\n if (!(await fileExists(archivePath))) {\n const msg = `Platforma Backend binary archive not found at '${archivePath}'`;\n logger.error(msg);\n throw new Error(msg);\n }\n\n const markerFilePath = upath.join(dstFolder, MarkerFileName);\n\n if (await fileExists(markerFilePath)) {\n logger.info(`Platforma Backend binaries unpack skipped: '${dstFolder}' exists`);\n return;\n }\n\n if (await fileExists(dstFolder)) {\n logger.info(`Removing previous incompletely unpacked folder: '${dstFolder}'`);\n await fsp.rm(dstFolder, { recursive: true });\n }\n\n logger.info(` creating target dir '${dstFolder}'`);\n await fsp.mkdir(dstFolder, { recursive: true });\n\n logger.info(\n `Unpacking Platforma Backend archive:\\n Archive: ${archivePath}\\n Target dir: ${dstFolder}`,\n );\n\n switch (archiveType) {\n case 'tgz':\n await tar.x({\n file: archivePath,\n cwd: dstFolder,\n gzip: true,\n });\n break;\n\n case 'zip':\n await decompress(archivePath, dstFolder);\n break;\n\n default:\n assertNever(archiveType);\n }\n\n // writing marker file, to be able in the future detect that we completely unarchived the tar file\n await fsp.writeFile(markerFilePath, 'ok');\n\n logger.info(` ... unpack done.`);\n}\n\nexport type ArchiveType = 'tgz' | 'zip';\n\nconst osToArchiveType: Record<OSType, ArchiveType> = {\n linux: 'tgz',\n macos: 'tgz',\n windows: 'zip',\n};\n","declare const PL_VERSION: string;\n\nexport function getDefaultPlVersion(): string {\n return PL_VERSION;\n}\n","import type { MiLogger } from '@milaboratories/ts-helpers';\nimport { assertNever } from '@milaboratories/ts-helpers';\nimport { downloadBinary } from './pl_binary_download';\nimport { getDefaultPlVersion } from './pl_version';\nimport os from 'os';\nimport upath from 'upath';\nimport type { OSType } from './os_and_arch';\nimport { newOs } from './os_and_arch';\n\n/** Shows how the binary should be got. */\nexport type PlBinarySource = PlBinarySourceDownload | PlBinarySourceLocal;\n\nexport type PlBinarySourceDownload = {\n readonly type: 'Download';\n readonly version: string;\n};\n\nexport type PlBinarySourceLocal = {\n readonly type: 'Local';\n readonly path: string;\n};\n\nexport function newDefaultPlBinarySource(): PlBinarySourceDownload {\n return { type: 'Download', version: getDefaultPlVersion() };\n}\n\nexport async function resolveLocalPlBinaryPath(\n logger: MiLogger,\n downloadDir: string,\n src: PlBinarySource,\n): Promise<string> {\n switch (src.type) {\n case 'Download':\n const ops = await downloadBinary(logger, downloadDir, 'pl', `pl-${src.version}`, os.arch(), os.platform());\n return upath.join(ops.baseName, 'binaries', osToBinaryName[newOs(os.platform())]);\n\n case 'Local':\n return src.path;\n\n default:\n assertNever(src);\n }\n}\n\nexport const osToBinaryName: Record<OSType, string> = {\n linux: 'platforma',\n macos: 'platforma',\n windows: 'platforma.exe',\n};\n","import { fileExists } from '@milaboratories/ts-helpers';\nimport fs from 'fs/promises';\nimport upath from 'upath';\n\nexport function filePid(dir: string) {\n return upath.join(dir, 'pl_pid');\n}\n\nexport async function readPid(filePath: string): Promise<number | undefined> {\n if (!(await fileExists(filePath))) {\n return undefined;\n }\n\n const text = await fs.readFile(filePath);\n\n return Number(text.toString());\n}\n\nexport async function writePid(filePath: string, pid: number) {\n await fs.writeFile(filePath, JSON.stringify(pid));\n}\n","import type { MiLogger } from '@milaboratories/ts-helpers';\n\n/** Records all inputs and outputs of one's choice, so if the error happened\n * one can check how it was by just printing this structure. */\nexport type Trace = Record<string, any>;\n\nexport function newTrace(): Trace {\n return {};\n}\n\nexport function trace(t: Trace, k: string, v: any) {\n t[k] = v;\n return v;\n}\n\n/** Creates a trace and runs a function with it. The function can record all its\n * logs or traces using `trace` fn. */\nexport async function withTrace<T>(\n logger: MiLogger,\n fn: (trace: (k: string, v: any) => typeof v, t: Trace) => Promise<T>,\n): Promise<T> {\n const t = newTrace();\n try {\n const result = await fn((k: string, v: any) => trace(t, k, v), t);\n return result;\n } catch (e: any) {\n logger.error(`error ${e} while doing traced operation, state: ${JSON.stringify(t)}`);\n throw e;\n }\n}\n","import type {\n ProcessOptions } from './process';\nimport {\n isProcessAlive,\n processStop,\n processWaitStopped,\n processRun,\n} from './process';\nimport type { PlBinarySource } from '../common/pl_binary';\nimport { newDefaultPlBinarySource, resolveLocalPlBinaryPath } from '../common/pl_binary';\nimport type { MiLogger } from '@milaboratories/ts-helpers';\nimport { notEmpty } from '@milaboratories/ts-helpers';\nimport type { ChildProcess, SpawnOptions } from 'child_process';\nimport { filePid, readPid, writePid } from './pid';\nimport type { Trace } from './trace';\nimport { withTrace } from './trace';\nimport upath from 'upath';\nimport fsp from 'fs/promises';\nimport type { Required } from 'utility-types';\n\nexport const LocalConfigYaml = 'config-local.yaml';\n\n/**\n * Represents a local running pl-core,\n * and has methods to start, check if it's running, stop and wait for stopping it.\n * Also, a hook on pl-core closed can be provided.\n */\nexport class LocalPl {\n private instance?: ChildProcess;\n public pid?: number;\n private nRuns: number = 0;\n private lastRunHistory: Trace = {};\n private wasStopped = false;\n\n constructor(\n private readonly logger: MiLogger,\n private readonly workingDir: string,\n private readonly startOptions: ProcessOptions,\n private readonly initialStartHistory: Trace,\n private readonly onClose?: (pl: LocalPl) => Promise<void>,\n private readonly onError?: (pl: LocalPl) => Promise<void>,\n private readonly onCloseAndError?: (pl: LocalPl) => Promise<void>,\n private readonly onCloseAndErrorNoStop?: (pl: LocalPl) => Promise<void>,\n ) {}\n\n async start() {\n await withTrace(this.logger, async (trace, t) => {\n this.wasStopped = false;\n const instance = processRun(this.logger, this.startOptions);\n instance.on('error', (e: any) => {\n this.logger.error(\n `error '${e}', while running platforma, started opts: ${JSON.stringify(this.debugInfo())}`,\n );\n\n // keep in mind there are no awaits here, it will be asynchronous\n if (this.onError !== undefined) this.onError(this);\n if (this.onCloseAndError !== undefined) this.onCloseAndError(this);\n if (this.onCloseAndErrorNoStop !== undefined && !this.wasStopped)\n this.onCloseAndErrorNoStop(this);\n });\n instance.on('close', () => {\n this.logger.warn(`platforma was closed, started opts: ${JSON.stringify(this.debugInfo())}`);\n\n // keep in mind there are no awaits here, it will be asynchronous\n if (this.onClose !== undefined) this.onClose(this);\n if (this.onCloseAndError !== undefined) this.onCloseAndError(this);\n if (this.onCloseAndErrorNoStop !== undefined && !this.wasStopped)\n this.onCloseAndErrorNoStop(this);\n });\n\n trace('started', true);\n\n const pidFile = trace('pidFile', filePid(this.workingDir));\n trace('pid', notEmpty(instance.pid));\n trace('pidWritten', await writePid(pidFile, notEmpty(instance.pid)));\n\n this.nRuns++;\n this.instance = instance;\n this.pid = instance.pid;\n this.lastRunHistory = t;\n });\n }\n\n stop() {\n // TODO use this.instance to stop the process\n this.wasStopped = true;\n processStop(notEmpty(this.pid));\n }\n\n async waitStopped() {\n await processWaitStopped(notEmpty(this.pid), 15000);\n }\n\n stopped() {\n return this.wasStopped;\n }\n\n async isAlive(): Promise<boolean> {\n return await isProcessAlive(notEmpty(this.pid));\n }\n\n debugInfo() {\n return {\n lastRunHistory: this.lastRunHistory,\n nRuns: this.nRuns,\n pid: this.pid,\n workingDir: this.workingDir,\n initialStartHistory: this.initialStartHistory,\n wasStopped: this.wasStopped,\n };\n }\n}\n\n/** Options to start a local pl-core. */\nexport type LocalPlOptions = {\n /** From what directory start a process. */\n readonly workingDir: string;\n /** A string representation of yaml config. */\n readonly config: string;\n /** How to get a binary, download it or get an existing one (default: download latest version) */\n readonly plBinary?: PlBinarySource;\n /** Additional options for a process, environments, stdout, stderr etc. */\n readonly spawnOptions?: SpawnOptions;\n /**\n * If the previous pl-core was started from the same directory,\n * we can check if it's still running and then stop it before starting a new one.\n * (default: true)\n */\n readonly closeOld?: boolean;\n\n readonly onClose?: (pl: LocalPl) => Promise<void>;\n readonly onError?: (pl: LocalPl) => Promise<void>;\n readonly onCloseAndError?: (pl: LocalPl) => Promise<void>;\n readonly onCloseAndErrorNoStop?: (pl: LocalPl) => Promise<void>;\n};\n\ntype LocalPlOptionsFull = Required<LocalPlOptions, 'plBinary' | 'spawnOptions' | 'closeOld'>;\n\n/**\n * Starts pl-core, if the option was provided downloads a binary, reads license environments etc.\n */\nexport async function localPlatformaInit(logger: MiLogger, _ops: LocalPlOptions): Promise<LocalPl> {\n // filling-in default values\n const ops = {\n plBinary: newDefaultPlBinarySource(),\n spawnOptions: {},\n closeOld: true,\n ..._ops,\n } satisfies LocalPlOptionsFull;\n\n return await withTrace(logger, async (trace, t) => {\n trace('startOptions', { ...ops, config: 'too wordy' });\n\n const workDir = upath.resolve(ops.workingDir);\n\n if (ops.closeOld) {\n trace('closeOld', await localPlatformaReadPidAndStop(logger, workDir));\n }\n\n const configPath = upath.join(workDir, LocalConfigYaml);\n\n logger.info(`writing configuration '${configPath}'...`);\n await fsp.writeFile(configPath, ops.config);\n\n const plBinPath = upath.join(workDir, 'binaries');\n const baseBinaryPath = await resolveLocalPlBinaryPath(logger, plBinPath, ops.plBinary);\n\n const binaryPath = trace('binaryPath', upath.join('binaries', baseBinaryPath));\n\n const processOpts: ProcessOptions = {\n cmd: binaryPath,\n args: ['-config', configPath],\n opts: {\n env: { ...process.env },\n cwd: workDir,\n stdio: ['pipe', 'ignore', 'inherit'],\n windowsHide: true, // hide a terminal on Windows\n ...ops.spawnOptions,\n },\n };\n trace('processOpts', {\n cmd: processOpts.cmd,\n args: processOpts.args,\n cwd: processOpts.opts.cwd,\n });\n\n const pl = new LocalPl(\n logger,\n ops.workingDir,\n processOpts,\n t,\n ops.onClose,\n ops.onError,\n ops.onCloseAndError,\n ops.onCloseAndErrorNoStop,\n );\n await pl.start();\n\n return pl;\n });\n}\n\n/** Reads a pid of the old pl-core if it was started in the same working directory,\n * and closes it. */\nasync function localPlatformaReadPidAndStop(\n logger: MiLogger,\n workingDir: string,\n): Promise<Record<string, any>> {\n return await withTrace(logger, async (trace, t) => {\n const file = trace('pidFilePath', filePid(workingDir));\n\n const oldPid = trace('pid', await readPid(file));\n const alive = trace('wasAlive', await isProcessAlive(oldPid));\n\n if (oldPid !== undefined && alive) {\n trace('stopped', processStop(oldPid));\n trace('waitStopped', await processWaitStopped(oldPid, 10_000));\n }\n\n return t;\n });\n}\n","import type { ConnectConfig, ClientChannel, SFTPWrapper } from 'ssh2';\nimport ssh, { Client } from 'ssh2';\nimport net from 'net';\nimport dns from 'dns';\nimport fs from 'fs';\nimport { readFile } from 'fs/promises';\nimport upath from 'upath';\nimport { RetryablePromise, type MiLogger } from '@milaboratories/ts-helpers';\nimport { randomBytes } from 'crypto';\n\nconst defaultConfig: ConnectConfig = {\n keepaliveInterval: 60000,\n keepaliveCountMax: 10,\n};\n\nexport type SshAuthMethods = 'publickey' | 'password';\nexport type SshAuthMethodsResult = SshAuthMethods[];\nexport type SshDirContent = {\n files: string[];\n directories: string[];\n};\n\nexport class SshClient {\n private config?: ConnectConfig;\n public homeDir?: string;\n private forwardedServers: net.Server[] = [];\n\n constructor(\n private readonly logger: MiLogger,\n private readonly client: Client,\n ) {}\n\n /**\n * Initializes the SshClient and establishes a connection using the provided configuration.\n * @param config - The connection configuration object for the SSH client.\n * @returns A new instance of SshClient with an active connection.\n */\n public static async init(logger: MiLogger, config: ConnectConfig): Promise<SshClient> {\n const withDefaults = {\n ...defaultConfig,\n ...config,\n };\n\n const client = new SshClient(logger, new Client());\n await client.connect(withDefaults);\n\n return client;\n }\n\n public getForwardedServers() {\n return this.forwardedServers;\n }\n\n public getFullHostName() {\n return `${this.config?.host}:${this.config?.port}`;\n }\n\n public getUserName() {\n return this.config?.username;\n }\n\n /**\n * Connects to the SSH server using the specified configuration.\n * @param config - The connection configuration object for the SSH client.\n * @returns A promise that resolves when the connection is established or rejects on error.\n */\n public async connect(config: ConnectConfig) {\n this.config = config;\n return await connect(this.client, config, () => {}, () => {});\n }\n\n /**\n * Executes a command on the SSH server.\n * @param command - The command to execute on the remote server.\n * @returns A promise resolving with the command's stdout and stderr outputs.\n */\n public async exec(command: string): Promise<SshExecResult> {\n return new Promise((resolve, reject) => {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n this.client.exec(command, (err: any, stream: ClientChannel) => {\n if (err) {\n return reject(`ssh.exec: ${command}, error occurred: ${err}`);\n }\n\n let stdout = '';\n let stderr = '';\n\n stream.on('close', (code: number) => {\n if (code === 0) {\n resolve({ stdout, stderr });\n } else {\n reject(new Error(`Command ${command} exited with code ${code}`));\n }\n }).on('data', (data: ArrayBuffer) => {\n stdout += data.toString();\n }).stderr.on('data', (data: ArrayBuffer) => {\n stderr += data.toString();\n });\n });\n });\n }\n\n /**\n * Retrieves the supported authentication methods for a given host and port.\n * @param host - The hostname or IP address of the server.\n * @param port - The port number to connect to on the server.\n * @returns 'publickey' | 'password'[] A promise resolving with a list of supported authentication methods.\n */\n public static async getAuthTypes(host: string, port: number): Promise<SshAuthMethodsResult> {\n return new Promise((resolve) => {\n let stdout = '';\n const conn = new Client();\n\n conn.on('ready', () => {\n conn.end();\n const types = this.extractAuthMethods(stdout);\n resolve(types.length === 0 ? ['publickey', 'password'] : types as SshAuthMethodsResult);\n });\n\n conn.on('error', () => {\n conn.end();\n resolve(['publickey', 'password']);\n });\n\n conn.connect({\n host,\n port,\n username: new Date().getTime().toString(),\n debug: (err) => {\n stdout += `${err}\\n`;\n },\n });\n });\n }\n\n /**\n * Extracts authentication methods from debug logs.\n * @param log - The debug log output containing authentication information.\n * @returns An array of extracted authentication methods.\n */\n private static extractAuthMethods(log: string): string[] {\n const match = log.match(/Inbound: Received USERAUTH_FAILURE \\((.+)\\)/);\n return match && match[1] ? match[1].split(',').map((method) => method.trim()) : [];\n }\n\n /**\n * Sets up port forwarding between a remote port on the SSH server and a local port.\n * A new connection is used for this operation instead of an existing one.\n * @param ports - An object specifying the remote and local port configuration.\n * @param config - Optional connection configuration for the SSH client.\n * @returns { server: net.Server } A promise resolving with the created server instance.\n */\n public async forwardPort(ports: { remotePort: number; localPort: number; localHost?: string }, config?: ConnectConfig): Promise<{ server: net.Server }> {\n const log = `ssh.forward:${ports.localPort}:${ports.remotePort}.id_${randomBytes(1).toString('hex')}`;\n config = config ?? this.config;\n\n // we make this thing persistent so that if the connection\n // drops (it happened in the past because of lots of errors and forwardOut opened channels),\n // we'll recreate it here.\n const persistentClient = new RetryablePromise((p: RetryablePromise<Client>) => {\n return new Promise<Client>((resolve, reject) => {\n const client = new Client();\n\n client.on('ready', () => {\n this.logger.info(`${log}.client.ready`);\n resolve(client);\n });\n\n client.on('error', (err) => {\n this.logger.info(`${log}.client.error: ${err}`);\n p.reset();\n reject(err);\n });\n\n client.on('close', () => {\n this.logger.info(`${log}.client.closed`);\n p.reset();\n });\n\n client.connect(config!);\n });\n });\n\n await persistentClient.ensure(); // warm up a connection\n\n return new Promise((resolve, reject) => {\n const server = net.createServer({ pauseOnConnect: true }, async (localSocket) => {\n const sockLog = `${log}.sock_${randomBytes(1).toString('hex')}`;\n // this.logger.info(`${sockLog}.localSocket: start connection`);\n let conn: Client;\n try {\n conn = await persistentClient.ensure();\n } catch (e: unknown) {\n this.logger.info(`${sockLog}.persistentClient.catch: ${e}`);\n localSocket.end();\n return;\n }\n\n let stream: ClientChannel;\n try {\n stream = await forwardOut(this.logger, conn, '127.0.0.1', 0, '127.0.0.1', ports.remotePort);\n } catch (e: unknown) {\n this.logger.error(`${sockLog}.forwardOut.err: ${e}`);\n localSocket.end();\n return;\n }\n\n localSocket.pipe(stream);\n stream.pipe(localSocket);\n localSocket.resume();\n // this.logger.info(`${sockLog}.forwardOut: connected`);\n\n stream.on('error', (err: unknown) => {\n this.logger.error(`${sockLog}.stream.error: ${err}`);\n localSocket.end();\n stream.end();\n });\n stream.on('close', () => {\n // this.logger.info(`${sockLog}.stream.close: closed`);\n localSocket.end();\n stream.end();\n });\n localSocket.on('close', () => {\n this.logger.info(`${sockLog}.localSocket: closed`);\n localSocket.end();\n stream.end();\n });\n });\n\n server.listen(ports.localPort, '127.0.0.1', () => {\n this.logger.info(`${log}.server: started listening`);\n this.forwardedServers.push(server);\n resolve({ server });\n });\n\n server.on('error', (err) => {\n server.close();\n reject(new Error(`${log}.server: error: ${JSON.stringify(err)}`));\n });\n\n server.on('close', () => {\n this.logger.info(`${log}.server: closed ${JSON.stringify(ports)}`);\n this.forwardedServers = this.forwardedServers.filter((s) => s !== server);\n });\n });\n }\n\n public closeForwardedPorts(): void {\n this.logger.info('[SSH] Closing all forwarded ports...');\n this.forwardedServers.forEach((server) => {\n const rawAddress = server.address();\n if (rawAddress && typeof rawAddress !== 'string') {\n const address: net.AddressInfo = rawAddress;\n this.logger.info(`[SSH] Closing port forward for server ${address.address}:${address.port}`);\n }\n\n server.close();\n });\n this.forwardedServers = [];\n }\n\n /**\n * Checks if a specified host is available by performing a DNS lookup.\n * @param hostname - The hostname or IP address to check.\n * @returns A promise resolving with `true` if the host is reachable, otherwise `false`.\n */\n public static async checkHostAvailability(hostname: string): Promise<boolean> {\n return new Promise((resolve) => {\n dns.lookup(hostname, (err) => {\n resolve(!err);\n });\n });\n }\n\n /**\n * Determines whether a private key requires a passphrase for use.\n * @param privateKey - The private key content to check.\n * @returns A promise resolving with `true` if a passphrase is required, otherwise `false`.\n */\n public static async isPassphraseRequiredForKey(privateKey: string): Promise<boolean> {\n return new Promise((resolve, reject) => {\n try {\n const keyOrError = ssh.utils.parseKey(privateKey);\n if (keyOrError instanceof Error) {\n resolve(true);\n }\n return resolve(false);\n } catch (err: unknown) {\n console.log('Error parsing privateKey');\n reject(new Error(`ssh.isPassphraseRequiredForKey: err ${err}`));\n }\n });\n }\n\n /**\n * Uploads a local file to a remote server via SFTP.\n * This function creates new SFTP connection\n * @param localPath - The local file path.\n * @param remotePath - The remote file path on the server.\n * @returns A promise resolving with `true` if the file was successfully uploaded.\n */\n public async uploadFile(localPath: string, remotePath: string): Promise<boolean> {\n return await this.withSftp(async (sftp) => {\n return new Promise((resolve, reject) => {\n sftp.fastPut(localPath, remotePath, (err) => {\n if (err) {\n const newErr = new Error(\n `ssh.uploadFile: err: ${err}, localPath: ${localPath}, remotePath: ${remotePath}`);\n return reject(newErr);\n }\n resolve(true);\n });\n });\n });\n }\n\n public async withSftp<R>(callback: (sftp: SFTPWrapper) => Promise<R>): Promise<R> {\n return new Promise((resolve, reject) => {\n this.client.sftp((err, sftp) => {\n if (err) {\n return reject(new Error(`ssh.withSftp: sftp err: ${err}`));\n }\n\n callback(sftp)\n .then(resolve)\n .catch((err) => {\n reject(new Error(`ssh.withSftp.callback: err ${err}`));\n })\n .finally(() => {\n sftp?.end();\n });\n });\n });\n }\n\n public async writeFileOnTheServer(remotePath: string, data: string | Buffer, mode: number = 0o660) {\n return this.withSftp(async (sftp) => {\n return this.writeFile(sftp, remotePath, data, mode);\n });\n }\n\n public async getForderStructure(sftp: SFTPWrapper, remotePath: string, data: SshDirContent = { files: [], directories: [] }): Promise<SshDirContent> {\n return new Promise((resolve, reject) => {\n sftp.readdir(remotePath, async (err, items) => {\n if (err) {\n return reject(err);\n }\n\n for (const item of items) {\n const itemPath = `${remotePath}/${item.filename}`;\n if (item.attrs.isDirectory()) {\n data.directories.push(itemPath);\n try {\n await this.getForderStructure(sftp, itemPath, data);\n } catch (error) {\n return reject(error);\n }\n } else {\n data.files.push(itemPath);\n }\n }\n resolve(data);\n });\n });\n }\n\n public rmdir(sftp: SFTPWrapper, path: string) {\n return new Promise((resolve, reject) => {\n sftp.rmdir(path, (err) => err ? reject(err) : resolve(true));\n });\n }\n\n public unlink(sftp: SFTPWrapper, path: string) {\n return new Promise((resolve, reject) => {\n sftp.unlink(path, (err) => err ? reject(err) : resolve(true));\n });\n }\n\n public async deleteFolder(path: string) {\n return this.withSftp(async (sftp) => {\n try {\n const list = await this.getForderStructure(sftp, path);\n this.logger.info(`ssh.deleteFolder list of files and directories`);\n this.logger.info(`ssh.deleteFolder list of files: ${list.files}`);\n this.logger.info(`ssh.deleteFolder list of directories: ${list.directories}`);\n\n for (const filePath of list.files) {\n this.logger.info(`ssh.deleteFolder unlink file ${filePath}`);\n await this.unlink(sftp, filePath);\n }\n\n list.directories.sort((a, b) => b.length - a.length);\n\n for (const directoryPath of list.directories) {\n this.logger.info(`ssh.deleteFolder rmdir ${directoryPath}`);\n await this.rmdir(sftp, directoryPath);\n }\n\n await this.rmdir(sftp, path);\n return true;\n } catch (e: unknown) {\n this.logger.error(e);\n const message = e instanceof Error ? e.message : '';\n throw new Error(`ssh.deleteFolder: path: ${path}, message: ${message}`);\n }\n });\n }\n\n public async readFile(remotePath: string): Promise<string> {\n return this.withSftp(async (sftp) => {\n return new Promise((resolve, reject) => {\n sftp.readFile(remotePath, (err, buffer) => {\n if (err) {\n return reject(new Error(`ssh.readFile: err occurred ${err}`));\n }\n resolve(buffer.toString());\n });\n });\n });\n }\n\n async chmod(path: string, mode: number) {\n return this.withSftp(async (sftp) => {\n return new Promise((resolve, reject) => {\n sftp.chmod(path, mode, (err) => {\n if (err) {\n return reject(new Error(`ssh.chmod: ${err}, path: ${path}, mode: ${mode}`));\n }\n return resolve(undefined);\n });\n });\n });\n }\n\n async checkFileExists(remotePath: string) {\n return this.withSftp(async (sftp) => {\n return new Promise((resolve, reject) => {\n sftp.stat(remotePath, (err: Error | undefined, stats) => {\n if (err) {\n if ((err as unknown as { code?: number })?.code === 2) {\n return resolve(false);\n }\n return reject(new Error(`ssh.checkFileExists: err ${err}`));\n }\n resolve(stats.isFile());\n });\n });\n });\n }\n\n async checkPathExists(remotePath: string): Promise<{ exists: boolean; isFile: boolean; isDirectory: boolean }> {\n return this.withSftp(async (sftp) => {\n return new Promise((resolve, reject) => {\n sftp.stat(remotePath, (err, stats) => {\n if (err) {\n if ((err as Error & { code: number }).code === 2) {\n return resolve({ exists: false, isFile: false, isDirectory: false });\n }\n return reject(new Error(`ssh.checkPathExists: ${err}`));\n }\n resolve({\n exists: true,\n isFile: stats.isFile(),\n isDirectory: stats.isDirectory(),\n });\n });\n });\n });\n }\n\n private async writeFile(sftp: SFTPWrapper, remotePath: string, data: string | Buffer, mode: number = 0o660): Promise<boolean> {\n return new Promise((resolve, reject) => {\n sftp.writeFile(remotePath, data, { mode }, (err) => {\n if (err) {\n return reject(new Error(`ssh.writeFile: err ${err}, remotePath: ${remotePath}`));\n }\n resolve(true);\n });\n });\n }\n\n public uploadFileUsingExistingSftp(sftp: SFTPWrapper, localPath: string, remotePath: string, mode: number = 0o660) {\n return new Promise((resolve, reject) => {\n readFile(localPath).then(async (result: Buffer) => {\n this.writeFile(sftp, remotePath, result, mode)\n .then(() => {\n resolve(undefined);\n })\n .catch((err) => {\n const msg = `uploadFileUsingExistingSftp: error ${err} occurred`;\n this.logger.error(msg);\n reject(new Error(msg));\n });\n });\n });\n }\n\n private async __uploadDirectory(sftp: SFTPWrapper, localDir: string, remoteDir: string, mode: number = 0o660): Promise<void> {\n return new Promise((resolve, reject) => {\n fs.readdir(localDir, async (err, files) => {\n if (err) {\n return reject(new Error(`ssh.__uploadDir: err ${err}, localDir: ${localDir}, remoteDir: ${remoteDir}`));\n }\n\n try {\n await this.__createRemoteDirectory(sftp, remoteDir);\n for (const file of files) {\n const localPath = upath.join(localDir, file);\n const remotePath = `${remoteDir}/${file}`;\n\n if (fs.lstatSync(localPath).isDirectory()) {\n await this.__uploadDirectory(sftp, localPath, remotePath, mode);\n } else {\n await this.uploadFileUsingExistingSftp(sftp, localPath, remotePath, mode);\n }\n }\n\n resolve();\n } catch (err) {\n const msg = `ssh.__uploadDir: catched err ${err}`;\n this.logger.error(msg);\n reject(new Error(msg));\n }\n });\n });\n }\n\n /**\n * Uploads a local directory and its contents (including subdirectories) to the remote server via SFTP.\n * @param localDir - The path to the local directory to upload.\n * @param remoteDir - The path to the remote directory on the server.\n * @returns A promise that resolves when the directory and its contents are uploaded.\n */\n public async uploadDirectory(localDir: string, remoteDir: string, mode: number = 0o660): Promise<void> {\n return new Promise((resolve, reject) => {\n this.withSftp(async (sftp: SFTPWrapper) => {\n try {\n await this.__uploadDirectory(sftp, localDir, remoteDir, mode);\n resolve();\n } catch (e: unknown) {\n reject(new Error(`ssh.uploadDirectory: ${e}`));\n }\n });\n });\n }\n\n /**\n * Ensures that a remote directory and all its parent directories exist.\n * @param sftp - The SFTP wrapper.\n * @param remotePath - The path to the remote directory.\n * @returns A promise that resolves when the directory is created.\n */\n private __createRemoteDirectory(sftp: SFTPWrapper, remotePath: string): Promise<void> {\n return new Promise((resolve, reject) => {\n const directories = remotePath.split('/');\n let currentPath = '';\n\n const createNext = (index: number) => {\n if (index >= directories.length) {\n return resolve();\n }\n\n currentPath += `${directories[index]}/`;\n\n sftp.stat(currentPath, (err) => {\n if (err) {\n sftp.mkdir(currentPath, (err) => {\n if (err) {\n return reject(new Error(`ssh.__createRemDir: err ${err}, remotePath: ${remotePath}`));\n }\n createNext(index + 1);\n });\n } else {\n createNext(index + 1);\n }\n });\n };\n\n createNext(0);\n });\n }\n\n /**\n * Ensures that a remote directory and all its parent directories exist.\n * @param sftp - The SFTP wrapper.\n * @param remotePath - The path to the remote directory.\n * @returns A promise that resolves when the directory is created.\n */\n public ensureRemoteDirCreated(remotePath: string, mode: number = 0o755): Promise<void> {\n return this.withSftp(async (sftp) => {\n const directories = remotePath.split('/');\n let currentPath = '';\n\n for (const directory of directories) {\n currentPath += `${directory}/`;\n\n try {\n await new Promise<void>((resolve, reject) => {\n sftp.stat(currentPath, (err) => {\n if (!err) return resolve();\n\n sftp.mkdir(currentPath, { mode }, (err) => {\n if (err) {\n return reject(new Error(`ssh.createRemoteDir: err ${err}, remotePath: ${remotePath}`));\n }\n resolve();\n });\n });\n });\n } catch (error) {\n console.error(`Failed to create directory: ${currentPath}`, error);\n throw error;\n }\n }\n });\n }\n\n /**\n * Downloads a file from the remote server to a local path via SFTP.\n * @param remotePath - The remote file path on the server.\n * @param localPath - The local file path to save the file.\n * @returns A promise resolving with `true` if the file was successfully downloaded.\n */\n public async downloadFile(remotePath: string, localPath: string): Promise<boolean> {\n return this.withSftp(async (sftp) => {\n return new Promise((resolve, reject) => {\n sftp.fastGet(remotePath, localPath, (err) => {\n if (err) {\n return reject(new Error(`ssh.downloadFile: err ${err}, remotePath: ${remotePath}, localPath: ${localPath}`));\n }\n resolve(true);\n });\n });\n });\n }\n\n /**\n * Closes the SSH client connection and forwarded ports.\n */\n public close(): void {\n this.closeForwardedPorts();\n this.client.end();\n }\n}\n\nexport type SshExecResult = { stdout: string; stderr: string };\n\nasync function connect(\n client: Client,\n config: ConnectConfig,\n onError: (e: unknown) => void,\n onClose: () => void,\n): Promise<Client> {\n return new Promise((resolve, reject) => {\n client.on('ready', () => {\n resolve(client);\n });\n\n client.on('error', (err: unknown) => {\n onError(err);\n reject(new Error(`ssh.connect: error occurred: ${err}`));\n });\n\n client.on('close', () => {\n onClose();\n });\n\n client.connect(config);\n });\n}\n\nasync function forwardOut(logger: MiLogger, conn: Client, localHost: string, localPort: number, remoteHost: string, remotePort: number): Promise<ClientChannel> {\n return new Promise((resolve, reject) => {\n conn.forwardOut(localHost, localPort, remoteHost, remotePort, (err, stream) => {\n if (err) {\n logger.error(`forwardOut.error: ${err}`);\n return reject(err);\n }\n\n return resolve(stream);\n });\n });\n}\n","/** Just a lot of hardcoded paths of our current ssh deployment. */\n\nimport upath from 'upath';\nimport { newArch } from '../common/os_and_arch';\nimport { getDefaultPlVersion } from '../common/pl_version';\n\nexport const minioDirName = 'minio-2024-12-18T13-15-44Z';\nexport const supervisordDirName = 'supervisord-0.7.3';\nexport const supervisordSubDirName = 'supervisord_0.7.3_Linux_64-bit';\n\nexport function workDir(remoteHome: string) {\n return upath.join(remoteHome, '.platforma_ssh');\n}\n\nexport function binariesDir(remoteHome: string) {\n return upath.join(workDir(remoteHome), 'binaries');\n}\n\nexport function platformaBaseDir(remoteHome: string, arch: string) {\n return upath.join(binariesDir(remoteHome), `pl-${getDefaultPlVersion()}-${newArch(arch)}`);\n}\n\nexport function platformaDir(remoteHome: string, arch: string) {\n return upath.join(platformaBaseDir(remoteHome, arch), 'binaries');\n}\n\nexport function platformaBin(remoteHome: string, arch: string): string {\n return upath.join(platformaDir(remoteHome, arch), 'platforma');\n}\n\nexport function platformaConf(remoteHome: string): string {\n return upath.join(workDir(remoteHome), 'config.yaml');\n}\n\nexport function platformaFreePortBin(remoteHome: string, arch: string): string {\n return upath.join(platformaDir(remoteHome, arch), 'free-port');\n}\n\nexport function minioDir(remoteHome: string, arch: string) {\n return upath.join(binariesDir(remoteHome), `minio-2024-12-18T13-15-44Z-${newArch(arch)}`);\n}\n\nexport function minioBin(remoteHome: string, arch: string) {\n return upath.join(minioDir(remoteHome, arch), 'minio');\n}\n\nexport function supervisorBinDir(remoteHome: string, arch: string) {\n return upath.join(binariesDir(remoteHome), `supervisord-0.7.3-${newArch(arch)}`, supervisordSubDirName);\n}\n\nexport function supervisorBin(remoteHome: string, arch: string): string {\n return upath.join(supervisorBinDir(remoteHome, arch), 'supervisord');\n}\n\nexport function supervisorConf(remoteHome: string) {\n return upath.join(workDir(remoteHome), 'supervisor.conf');\n}\n\nexport function connectionInfo(remoteHome: string) {\n return upath.join(workDir(remoteHome), `connection.txt`);\n}\n","/** Provides helper functions to work with supervisord */\n\nimport type { MiLogger } from '@milaboratories/ts-helpers';\nimport * as plpath from './pl_paths';\nimport type { SshClient, SshExecResult } from './ssh';\nimport { randomBytes } from 'crypto';\n\nexport async function supervisorCtlStart(\n sshClient: SshClient,\n remoteHome: string, arch: string,\n) {\n const result = await supervisorExec(sshClient, remoteHome, arch, '--daemon');\n\n if (result.stderr) {\n throw new Error(`Can not run ssh Platforma ${result.stderr}`);\n }\n}\n\nexport async function supervisorStop(\n sshClient: SshClient,\n remoteHome: string, arch: string,\n) {\n const result = await supervisorExec(sshClient, remoteHome, arch, 'ctl shutdown');\n\n if (result.stderr) {\n throw new Error(`Can not stop ssh Platforma ${result.stderr}`);\n }\n}\n\n/** Provides a simple true/false response got from supervisord status\n * along with a debug info that could be showed in error logs (raw response from the command, parsed response etc). */\nexport type SupervisorStatus = {\n platforma?: boolean;\n minio?: boolean;\n allAlive: boolean; // true when both pl and minio are alive.\n rawResult?: SshExecResult;\n execError?: string;\n};\n\nexport async function supervisorStatus(\n logger: MiLogger,\n sshClient: SshClient,\n remoteHome: string, arch: string,\n): Promise<SupervisorStatus> {\n let result: SshExecResult;\n try {\n result = await supervisorExec(sshClient, remoteHome, arch, 'ctl status');\n } catch (e: unknown) {\n return { execError: String(e), allAlive: false };\n }\n\n if (result.stderr) {\n logger.info(`supervisord ctl status: stderr occurred: ${result.stderr}, stdout: ${result.stdout}`);\n\n return { rawResult: result, allAlive: false };\n }\n\n const platforma = isProgramRunning(result.stdout, 'platforma');\n const minio = isProgramRunning(result.stdout, 'minio');\n const status: SupervisorStatus = {\n rawResult: result,\n platforma,\n minio,\n allAlive: platforma && minio,\n };\n\n if (status.allAlive) {\n return status;\n }\n\n if (!status.minio) {\n logger.warn('Minio is not running on the server');\n }\n\n if (!status.platforma) {\n logger.warn('Platforma is not running on the server');\n }\n\n return status;\n}\n\nexport function generateSupervisordConfig(\n minioStorageDir: string,\n minioEnvs: Record<string, string>,\n supervisorRemotePort: number,\n remoteWorkDir: string,\n platformaConfigPath: string,\n\n minioPath: string,\n plPath: string,\n) {\n const minioEnvStr = Object.entries(minioEnvs).map(([key, value]) => `${key}=\"${value}\"`).join(',');\n const password = randomBytes(16).toString('hex');\n const freePort = supervisorRemotePort;\n\n return `\n[supervisord]\nlogfile=${remoteWorkDir}/supervisord.log\nloglevel=info\npidfile=${remoteWorkDir}/supervisord.pid\n\n[inet_http_server]\nport=127.0.0.1:${freePort}\nusername=default-user\npassword=${password}\n\n[supervisorctl]\nserverurl=http://127.0.0.1:${freePort}\nusername=default-user\npassword=${password}\n\n[program:platforma]\nautostart=true\ndepends_on=minio\ncommand=${plPath} --config ${platformaConfigPath}\ndirectory=${remoteWorkDir}\nautorestart=true\n\n[program:minio]\nautostart=true\nenvironment=${minioEnvStr}\ncommand=${minioPath} server ${minioStorageDir}\ndirectory=${remoteWorkDir}\nautorestart=true\n`;\n}\n\nexport async function supervisorExec(\n sshClient: SshClient,\n remoteHome: string, arch: string,\n command: string,\n) {\n const supervisorCmd = plpath.supervisorBin(remoteHome, arch);\n const supervisorConf = plpath.supervisorConf(remoteHome);\n\n const cmd = `${supervisorCmd} --configuration ${supervisorConf} ${command}`;\n return await sshClient.exec(cmd);\n}\n\nfunction isProgramRunning(output: string, programName: string) {\n // eslint-disable-next-line no-control-regex\n const stripAnsi = (str: string) => str.replace(/\\x1B\\[[0-9;]*m/g, '');\n\n const cleanedOutput = stripAnsi(output);\n\n return cleanedOutput.split('\\n').some((line) => {\n const [name, status] = line.trim().split(/\\s{2,}/); // Split string by 2 spaces.\n\n return name === programName && status === 'Running';\n });\n}\n","/** We store all info about the connection on the server,\n * so that another client could read the file and connect from another machine. */\nimport { z } from 'zod';\n\n//\n// Types\n//\n\nexport const PortPair = z.object({\n local: z.number(),\n remote: z.number(),\n});\n/** The pair of ports for forwarding. */\nexport type PortPair = z.infer<typeof PortPair>;\n\nexport const SshPlPorts = z.object({\n grpc: PortPair,\n monitoring: PortPair,\n debug: PortPair,\n minioPort: PortPair,\n minioConsolePort: PortPair,\n});\n/** All info about ports that are forwarded. */\nexport type SshPlPorts = z.infer<typeof SshPlPorts>;\n\nexport const ConnectionInfo = z.object({\n plUser: z.string(),\n plPassword: z.string(),\n ports: SshPlPorts,\n\n // It's false by default because it was added later,\n // and in some deployments there won't be useGlobalAccess flag in the file.\n useGlobalAccess: z.boolean().default(false),\n\n // We added the field afterwards, the pl backend was this version.\n plVersion: z.string().default('1.18.3'),\n});\n/** The content of the file that holds all the info about the connection on the remote server. */\nexport type ConnectionInfo = z.infer<typeof ConnectionInfo>;\n\n//\n// Funcs\n//\n\nexport function newConnectionInfo(\n plUser: string,\n plPassword: string,\n ports: SshPlPorts,\n useGlobalAccess: boolean,\n plVersion: string,\n): ConnectionInfo {\n return {\n plUser,\n plPassword,\n ports,\n useGlobalAccess,\n plVersion,\n };\n}\n\nexport function parseConnectionInfo(content: string): ConnectionInfo {\n return ConnectionInfo.parse(JSON.parse(content));\n}\n\nexport function stringifyConnectionInfo(conn: ConnectionInfo): string {\n return JSON.stringify(conn, undefined, 2);\n}\n","import type * as ssh from 'ssh2';\nimport { SshClient } from './ssh';\nimport type { MiLogger } from '@milaboratories/ts-helpers';\nimport { sleep, notEmpty } from '@milaboratories/ts-helpers';\nimport type { DownloadBinaryResult } from '../common/pl_binary_download';\nimport { downloadBinaryNoExtract } from '../common/pl_binary_download';\nimport upath from 'upath';\nimport * as plpath from './pl_paths';\nimport { getDefaultPlVersion } from '../common/pl_version';\n\nimport net from 'net';\nimport type { PlLicenseMode, SshPlConfigGenerationResult } from '@milaboratories/pl-config';\nimport { generateSshPlConfigs, getFreePort } from '@milaboratories/pl-config';\nimport type { SupervisorStatus } from './supervisord';\nimport { supervisorStatus, supervisorStop as supervisorCtlShutdown, generateSupervisordConfig, supervisorCtlStart } from './supervisord';\nimport type { ConnectionInfo, SshPlPorts } from './connection_info';\nimport { newConnectionInfo, parseConnectionInfo, stringifyConnectionInfo } from './connection_info';\nimport type { PlBinarySourceDownload } from '../common/pl_binary';\n\nexport class SshPl {\n private initState: PlatformaInitState = {};\n constructor(\n public readonly logger: MiLogger,\n public readonly sshClient: SshClient,\n private readonly username: string,\n ) { }\n\n public info() {\n return {\n username: this.username,\n initState: this.initState,\n };\n }\n\n public static async init(logger: MiLogger, config: ssh.ConnectConfig): Promise<SshPl> {\n try {\n const sshClient = await SshClient.init(logger, config);\n return new SshPl(logger, sshClient, notEmpty(config.username));\n } catch (e: unknown) {\n logger.error(`Connection error in SshClient.init: ${e}`);\n throw e;\n }\n }\n\n public cleanUp() {\n this.sshClient.close();\n }\n\n /** Provides an info if the platforma and minio are running along with the debug info. */\n public async isAlive(): Promise<SupervisorStatus> {\n const arch = await this.getArch();\n const remoteHome = await this.getUserHomeDirectory();\n return await supervisorStatus(this.logger, this.sshClient, remoteHome, arch.arch);\n }\n\n /** Starts all the services on the server.\n * Idempotent semantic: we could call it several times. */\n public async start() {\n const arch = await this.getArch();\n const remoteHome = await this.getUserHomeDirectory();\n\n try {\n if (!(await this.isAlive()).allAlive) {\n await supervisorCtlStart(this.sshClient, remoteHome, arch.arch);\n\n // We are waiting for Platforma to run to ensure that it has started.\n return await this.checkIsAliveWithInterval();\n }\n } catch (e: unknown) {\n const msg = `SshPl.start: error occurred ${e}`;\n this.logger.error(msg);\n throw new Error(msg);\n }\n }\n\n /** Stops all the services on the server.\n * Idempotent semantic: we could call it several times. */\n public async stop() {\n const arch = await this.getArch();\n const remoteHome = await this.getUserHomeDirectory();\n\n try {\n if ((await this.isAlive()).allAlive) {\n await supervisorCtlShutdown(this.sshClient, remoteHome, arch.arch);\n return await this.checkIsAliveWithInterval(undefined, undefined, false);\n }\n } catch (e: unknown) {\n const msg = `PlSsh.stop: error occurred ${e}`;\n this.logger.error(msg);\n throw new Error(msg);\n }\n }\n\n /** Stops the services, deletes a directory with the state and closes SSH connection. */\n public async reset(): Promise<boolean> {\n await this.stopAndClean();\n this.cleanUp();\n return true;\n }\n\n /** Stops platforma and deletes its state. */\n public async stopAndClean(): Promise<void> {\n const remoteHome = await this.getUserHomeDirectory();\n\n this.logger.info(`pl.reset: Stop Platforma on the server`);\n await this.stop();\n\n this.logger.info(`pl.reset: Deleting Platforma workDir ${plpath.workDir(remoteHome)} on the server`);\n await this.sshClient.deleteFolder(plpath.workDir(remoteHome));\n }\n\n /** Downloads binaries and untar them on the server,\n * generates all the configs, creates necessary dirs,\n * and finally starts all the services. */\n public async platformaInit(options: SshPlConfig): Promise<ConnectionInfo> {\n const state: PlatformaInitState = { localWorkdir: options.localWorkdir };\n\n try {\n // merge options with default ops.\n const ops: SshPlConfig = {\n ...defaultSshPlConfig,\n ...options,\n };\n state.plBinaryOps = ops.plBinary;\n state.arch = await this.getArch();\n state.remoteHome = await this.getUserHomeDirectory();\n state.alive = await this.isAlive();\n\n if (state.alive.allAlive) {\n state.userCredentials = await this.getUserCredentials(state.remoteHome);\n if (!state.userCredentials) {\n throw new Error(`SshPl.platformaInit: platforma is alive but userCredentials are not found`);\n }\n const sameGA = state.userCredentials.useGlobalAccess == ops.useGlobalAccess;\n const samePlVersion = state.userCredentials.plVersion == ops.plBinary!.version;\n state.needRestart = !(sameGA && samePlVersion);\n this.logger.info(`SshPl.platformaInit: need restart? ${state.needRestart}`);\n\n if (!state.needRestart)\n return state.userCredentials;\n\n await this.stop();\n }\n\n const downloadRes = await this.downloadBinariesAndUploadToTheServer(\n ops.localWorkdir, ops.plBinary!, state.remoteHome, state.arch,\n );\n state.binPaths = { ...downloadRes, history: undefined };\n state.downloadedBinaries = downloadRes.history;\n\n state.ports = await this.fetchPorts(state.remoteHome, state.arch);\n\n if (!state.ports.debug.remote || !state.ports.grpc.remote || !state.ports.minioPort.remote || !state.ports.minioConsolePort.remote || !state.ports.monitoring.remote) {\n throw new Error(`SshPl.platformaInit: remote ports are not defined`);\n }\n\n const config = await generateSshPlConfigs({\n logger: this.logger,\n workingDir: plpath.workDir(state.remoteHome),\n portsMode: {\n type: 'customWithMinio',\n ports: {\n debug: state.ports.debug.remote,\n grpc: state.ports.grpc.remote,\n minio: state.ports.minioPort.remote,\n minioConsole: state.ports.minioConsolePort.remote,\n monitoring: state.ports.monitoring.remote,\n\n grpcLocal: state.ports.grpc.local,\n minioLocal: state.ports.minioPort.local,\n },\n },\n licenseMode: ops.license,\n useGlobalAccess: notEmpty(ops.useGlobalAccess),\n });\n state.generatedConfig = { ...config, filesToCreate: { skipped: 'it is too wordy' } };\n\n for (const [filePath, content] of Object.entries(config.filesToCreate)) {\n await this.sshClient.writeFileOnTheServer(filePath, content);\n this.logger.info(`Created file ${filePath}`);\n }\n\n for (const dir of config.dirsToCreate) {\n await this.sshClient.ensureRemoteDirCreated(dir);\n this.logger.info(`Created directory ${dir}`);\n }\n\n const supervisorConfig = generateSupervisordConfig(\n config.minioConfig.storageDir,\n config.minioConfig.envs,\n await this.getFreePortForPlatformaOnServer(state.remoteHome, state.arch),\n config.workingDir,\n config.plConfig.configPath,\n state.binPaths.minioRelPath,\n state.binPaths.downloadedPl,\n );\n\n const writeResult = await this.sshClient.writeFileOnTheServer(plpath.supervisorConf(state.remoteHome), supervisorConfig);\n if (!writeResult) {\n throw new Error(`Can not write supervisord config on the server ${plpath.workDir(state.remoteHome)}`);\n }\n\n state.connectionInfo = newConnectionInfo(\n config.plUser,\n config.plPassword,\n state.ports,\n notEmpty(ops.useGlobalAccess),\n ops.plBinary!.version,\n );\n await this.sshClient.writeFileOnTheServer(\n plpath.connectionInfo(state.remoteHome),\n stringifyConnectionInfo(state.connectionInfo),\n );\n\n await this.start();\n state.started = true;\n this.initState = state;\n\n return state.connectionInfo;\n } catch (e: unknown) {\n const msg = `SshPl.platformaInit: error occurred: ${e}, state: ${JSON.stringify(state)}`;\n this.logger.error(msg);\n\n throw new Error(msg);\n }\n }\n\n public async downloadBinariesAndUploadToTheServer(\n localWorkdir: string,\n plBinary: PlBinarySourceDownload,\n remoteHome: string,\n arch: Arch,\n ) {\n const state: DownloadAndUntarState[] = [];\n try {\n const pl = await this.downloadAndUntar(\n localWorkdir, remoteHome, arch,\n 'pl', `pl-${plBinary.version}`,\n );\n state.push(pl);\n\n const supervisor = await this.downloadAndUntar(\n localWorkdir, remoteHome, arch,\n 'supervisord', plpath.supervisordDirName,\n );\n state.push(supervisor);\n\n const minioPath = plpath.minioBin(remoteHome, arch.arch);\n const minio = await this.downloadAndUntar(\n localWorkdir, remoteHome, arch,\n 'minio', plpath.minioDirName,\n );\n state.push(minio);\n await this.sshClient.chmod(minioPath, 0o750);\n\n return {\n history: state,\n minioRelPath: minioPath,\n downloadedPl: plpath.platformaBin(remoteHome, arch.arch),\n };\n } catch (e: unknown) {\n const msg = `SshPl.downloadBinariesAndUploadToServer: error ${e} occurred, state: ${JSON.stringify(state)}`;\n this.logger.error(msg);\n throw e;\n }\n }\n\n /** We have to extract pl in the remote server,\n * because Windows doesn't support symlinks\n * that are found in Linux pl binaries tgz archive.\n * For this reason, we extract all to the remote server.\n * It requires `tar` to be installed on the server\n * (it's not installed for Rocky Linux for example). */\n public async downloadAndUntar(\n localWorkdir: string,\n remoteHome: string,\n arch: Arch,\n softwareName: string,\n tgzName: string,\n ): Promise<DownloadAndUntarState> {\n const state: DownloadAndUntarState = {};\n state.binBasePath = plpath.binariesDir(remoteHome);\n await this.sshClient.ensureRemoteDirCreated(state.binBasePath);\n state.binBasePathCreated = true;\n\n let downloadBinaryResult: DownloadBinaryResult | null = null;\n const attempts = 5;\n for (let i = 1; i <= attempts; i++) {\n try {\n downloadBinaryResult = await downloadBinaryNoExtract(\n this.logger,\n localWorkdir,\n softwareName,\n tgzName,\n arch.arch, arch.platform,\n );\n break;\n } catch (e: unknown) {\n await sleep(300);\n if (i == attempts) {\n throw new Error(`downloadAndUntar: ${attempts} attempts, last error: ${e}`);\n }\n }\n }\n state.downloadResult = notEmpty(downloadBinaryResult);\n\n state.localArchivePath = upath.resolve(state.downloadResult.archivePath);\n state.remoteDir = upath.join(state.binBasePath, state.downloadResult.baseName);\n state.remoteArchivePath = state.remoteDir + '.tgz';\n\n await this.sshClient.ensureRemoteDirCreated(state.remoteDir);\n await this.sshClient.uploadFile(state.localArchivePath, state.remoteArchivePath);\n state.uploadDone = true;\n\n // TODO: Create a proper archive to avoid xattr warnings\n const untarResult = await this.sshClient.exec(\n `tar --warning=no-all -xvf ${state.remoteArchivePath} --directory=${state.remoteDir}`,\n );\n\n if (untarResult.stderr)\n throw Error(`downloadAndUntar: untar: stderr occurred: ${untarResult.stderr}, stdout: ${untarResult.stdout}`);\n\n state.untarDone = true;\n\n return state;\n }\n\n public async needDownload(remoteHome: string, arch: Arch) {\n const checkPathSupervisor = plpath.supervisorBin(remoteHome, arch.arch);\n const checkPathMinio = plpath.minioDir(remoteHome, arch.arch);\n const checkPathPlatforma = plpath.platformaBin(remoteHome, arch.arch);\n\n if (!await this.sshClient.checkFileExists(checkPathPlatforma)\n || !await this.sshClient.checkFileExists(checkPathMinio)\n || !await this.sshClient.checkFileExists(checkPathSupervisor)) {\n return true;\n }\n\n return false;\n }\n\n public async checkIsAliveWithInterval(interval: number = 1000, count = 15, shouldStart = true): Promise<void> {\n const maxMs = count * interval;\n\n let total = 0;\n let alive = await this.isAlive();\n while (shouldStart ? !alive.allAlive : alive.allAlive) {\n await sleep(interval);\n total += interval;\n if (total > maxMs) {\n throw new Error(`isAliveWithInterval: The process did not ${shouldStart ? 'started' : 'stopped'} after ${maxMs} ms. Live status: ${JSON.stringify(alive)}`);\n }\n alive = await this.isAlive();\n }\n }\n\n public async getUserCredentials(remoteHome: string): Promise<ConnectionInfo> {\n const connectionInfo = await this.sshClient.readFile(plpath.connectionInfo(remoteHome));\n return parseConnectionInfo(connectionInfo);\n }\n\n public async fetchPorts(remoteHome: string, arch: Arch): Promise<SshPlPorts> {\n const ports: SshPlPorts = {\n grpc: {\n local: await getFreePort(),\n remote: await this.getFreePortForPlatformaOnServer(remoteHome, arch),\n },\n monitoring: {\n local: await getFreePort(),\n remote: await this.getFreePortForPlatformaOnServer(remoteHome, arch),\n },\n debug: {\n local: await getFreePort(),\n remote: await this.getFreePortForPlatformaOnServer(remoteHome, arch),\n },\n minioPort: {\n local: await getFreePort(),\n remote: await this.getFreePortForPlatformaOnServer(remoteHome, arch),\n },\n minioConsolePort: {\n local: await getFreePort(),\n remote: await this.getFreePortForPlatformaOnServer(remoteHome, arch),\n },\n };\n\n return ports;\n }\n\n public async getLocalFreePort(): Promise<number> {\n return new Promise((res) => {\n const srv = net.createServer();\n srv.listen(0, () => {\n const port = (srv.address() as net.AddressInfo).port;\n srv.close((_) => res(port));\n });\n });\n }\n\n public async getFreePortForPlatformaOnServer(remoteHome: string, arch: Arch): Promise<number> {\n const freePortBin = plpath.platformaFreePortBin(remoteHome, arch.arch);\n\n const { stdout, stderr } = await this.sshClient.exec(`${freePortBin}`);\n if (stderr) {\n throw new Error(`getFreePortForPlatformaOnServer: stderr is not empty: ${stderr}, stdout: ${stdout}`);\n }\n\n return +stdout;\n }\n\n public async getArch(): Promise<Arch> {\n const { stdout, stderr } = await this.sshClient.exec('uname -s && uname -m');\n if (stderr)\n throw new Error(`getArch: stderr is not empty: ${stderr}, stdout: ${stdout}`);\n\n const arr = stdout.split('\\n');\n\n return {\n platform: arr[0],\n arch: arr[1],\n };\n }\n\n public async getUserHomeDirectory() {\n const { stdout, stderr } = await this.sshClient.exec('echo $HOME');\n\n if (stderr) {\n const home = `/home/${this.username}`;\n console.warn(`getUserHomeDirectory: stderr is not empty: ${stderr}, stdout: ${stdout}, will get a default home: ${home}`);\n\n return home;\n }\n\n return stdout.trim();\n }\n}\n\ntype Arch = { platform: string; arch: string };\n\nexport type SshPlConfig = {\n localWorkdir: string;\n license: PlLicenseMode;\n useGlobalAccess?: boolean;\n plBinary?: PlBinarySourceDownload;\n};\n\nconst defaultSshPlConfig: Pick<\n SshPlConfig,\n | 'useGlobalAccess'\n | 'plBinary'\n> = {\n useGlobalAccess: false,\n plBinary: {\n type: 'Download',\n version: getDefaultPlVersion(),\n },\n};\n\ntype BinPaths = {\n history?: DownloadAndUntarState[];\n minioRelPath: string;\n downloadedPl: string;\n};\n\ntype DownloadAndUntarState = {\n binBasePath?: string;\n binBasePathCreated?: boolean;\n downloadResult?: DownloadBinaryResult;\n attempts?: number;\n\n localArchivePath?: string;\n remoteDir?: string;\n remoteArchivePath?: string;\n uploadDone?: boolean;\n untarDone?: boolean;\n};\n\ntype PlatformaInitState = {\n localWorkdir?: string;\n plBinaryOps?: PlBinarySourceDownload;\n arch?: Arch;\n remoteHome?: string;\n alive?: SupervisorStatus;\n userCredentials?: ConnectionInfo;\n needRestart?: boolean;\n downloadedBinaries?: DownloadAndUntarState[];\n binPaths?: BinPaths;\n ports?: SshPlPorts;\n generatedConfig?: SshPlConfigGenerationResult;\n connectionInfo?: ConnectionInfo;\n started?: boolean;\n};\n"],"names":["processRun","logger","opts","spawn","isProcessAlive","pid","processStop","processWaitStopped","maxMs","total","sleep","OSes","newOs","osName","Arches","newArch","arch","cdn","gaCdn","downloadBinaryNoExtract","baseDir","softwareName","tgzName","platform","getPathsForDownload","archiveUrl","alternativeArchiveGAUrl","archivePath","downloadArchive","downloadBinary","archiveName","archiveType","targetFolder","baseName","extractArchive","os","osToArchiveType","archiveFileName","upath","dstArchiveFile","state","fileExists","fsp","body","statusCode","request","textBody","text","Readable","Writable","fs","e","msg","MarkerFileName","dstFolder","markerFilePath","tar","decompress","assertNever","getDefaultPlVersion","newDefaultPlBinarySource","resolveLocalPlBinaryPath","downloadDir","src","ops","osToBinaryName","filePid","dir","readPid","filePath","writePid","newTrace","trace","t","k","v","withTrace","fn","LocalConfigYaml","LocalPl","workingDir","startOptions","initialStartHistory","onClose","onError","onCloseAndError","onCloseAndErrorNoStop","__publicField","instance","pidFile","notEmpty","localPlatformaInit","_ops","workDir","localPlatformaReadPidAndStop","configPath","plBinPath","baseBinaryPath","processOpts","pl","file","oldPid","alive","defaultConfig","SshClient","client","config","withDefaults","Client","_a","_b","connect","command","resolve","reject","err","stream","stdout","stderr","code","data","host","port","conn","types","log","match","method","ports","randomBytes","persistentClient","RetryablePromise","p","server","net","localSocket","sockLog","forwardOut","s","rawAddress","address","hostname","dns","privateKey","ssh","localPath","remotePath","sftp","newErr","callback","mode","items","item","itemPath","error","path","list","a","b","directoryPath","message","buffer","stats","readFile","result","localDir","remoteDir","files","directories","currentPath","createNext","index","directory","localHost","localPort","remoteHost","remotePort","minioDirName","supervisordDirName","supervisordSubDirName","remoteHome","binariesDir","platformaBaseDir","platformaDir","platformaBin","platformaFreePortBin","minioDir","minioBin","supervisorBinDir","supervisorBin","supervisorConf","connectionInfo","supervisorCtlStart","sshClient","supervisorExec","supervisorStop","supervisorStatus","platforma","isProgramRunning","minio","status","generateSupervisordConfig","minioStorageDir","minioEnvs","supervisorRemotePort","remoteWorkDir","platformaConfigPath","minioPath","plPath","minioEnvStr","key","value","password","freePort","supervisorCmd","plpath.supervisorBin","plpath.supervisorConf","cmd","output","programName","str","line","name","PortPair","z","SshPlPorts","ConnectionInfo","newConnectionInfo","plUser","plPassword","useGlobalAccess","plVersion","parseConnectionInfo","content","stringifyConnectionInfo","SshPl","username","supervisorCtlShutdown","plpath.workDir","options","defaultSshPlConfig","sameGA","samePlVersion","downloadRes","generateSshPlConfigs","supervisorConfig","plpath.connectionInfo","localWorkdir","plBinary","supervisor","plpath.supervisordDirName","plpath.minioBin","plpath.minioDirName","plpath.platformaBin","plpath.binariesDir","downloadBinaryResult","attempts","i","untarResult","checkPathSupervisor","checkPathMinio","plpath.minioDir","checkPathPlatforma","interval","count","shouldStart","getFreePort","res","srv","_","freePortBin","plpath.platformaFreePortBin","arr","home"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../src/local/process.ts","../src/common/os_and_arch.ts","../src/common/pl_binary_download.ts","../src/common/pl_version.ts","../src/common/pl_binary.ts","../src/local/pid.ts","../src/local/trace.ts","../src/local/pl.ts","../src/ssh/ssh.ts","../src/ssh/pl_paths.ts","../src/ssh/supervisord.ts","../src/ssh/connection_info.ts","../src/ssh/pl.ts"],"sourcesContent":["import type { SpawnOptions, ChildProcess } from 'child_process';\nimport { spawn } from 'child_process';\nimport type { MiLogger } from '@milaboratories/ts-helpers';\nimport { sleep } from '@milaboratories/ts-helpers';\n\nexport type ProcessOptions = {\n cmd: string;\n args: string[];\n opts: SpawnOptions;\n};\n\nexport function processRun(logger: MiLogger, opts: ProcessOptions): ChildProcess {\n logger.info(`Running:\ncmd: ${JSON.stringify([opts.cmd, ...opts.args])}\nwd: ${opts.opts.cwd}`);\n\n logger.info(' spawning child process');\n return spawn(opts.cmd, opts.args, opts.opts);\n}\n\nexport async function isProcessAlive(pid: number) {\n try {\n process.kill(pid, 0);\n return true;\n } catch (e) {\n return false;\n }\n}\n\nexport function processStop(pid: number) {\n return process.kill(pid, 'SIGINT');\n}\n\nexport async function processWaitStopped(pid: number, maxMs: number) {\n const sleepMs = 100;\n let total = 0;\n while (await isProcessAlive(pid)) {\n await sleep(sleepMs);\n total += sleepMs;\n if (total > maxMs) {\n throw new Error(`The process did not stopped after ${maxMs} ms.`);\n }\n }\n}\n","import os from 'os';\n\nexport const OSes = ['linux', 'macos', 'windows'] as const;\nexport type OSType = (typeof OSes)[number];\n\n/** @param osName - should be the thing returned from either {@link os.platform())} or `uname -s` */\nexport function newOs(osName: string): OSType {\n switch (osName.toLowerCase()) {\n case 'darwin':\n return 'macos';\n case 'linux':\n return 'linux';\n case 'win32':\n return 'windows';\n default:\n throw new Error(\n `operating system '${osName}' is not currently supported by Platforma ecosystem. The list of OSes supported: `\n + JSON.stringify(OSes),\n );\n }\n}\n\nexport const Arches = ['amd64', 'arm64'] as const;\nexport type ArchType = (typeof Arches)[number];\n\n/** @param arch - should be the thing returned from either {@link os.arch())} or `uname -m` */\nexport function newArch(arch: string): ArchType {\n switch (arch) {\n case 'aarch64':\n case 'aarch64_be':\n case 'arm64':\n return 'arm64';\n\n case 'x86_64':\n case 'x64':\n return 'amd64';\n\n default:\n throw new Error(\n `processor architecture '${arch}' is not currently supported by Platforma ecosystem. The list of architectures supported: `\n + JSON.stringify(Arches),\n );\n }\n}\n","import fs from 'fs';\nimport fsp from 'fs/promises';\nimport upath from 'upath';\nimport { request } from 'undici';\nimport { Writable, Readable } from 'stream';\nimport { text } from 'stream/consumers';\nimport * as tar from 'tar';\nimport type { MiLogger } from '@milaboratories/ts-helpers';\nimport { assertNever, fileExists } from '@milaboratories/ts-helpers';\nimport decompress from 'decompress';\nimport type { ArchType, OSType } from './os_and_arch';\nimport { newOs, newArch } from './os_and_arch';\n\nconst cdn = 'https://cdn.platforma.bio/software';\n// We'll download things from Global Access if downloading from CDN has failed\n// (it might be that it's blocked from the company's network.)\nconst gaCdn = 'https://cdn-ga.pl-open.science/software';\n\nexport type DownloadBinaryResult = {\n archiveUrl: string;\n alternativeArchiveGAUrl: string;\n wasDownloadedFrom?: string;\n archivePath: string;\n archiveType: ArchiveType;\n targetFolder: string;\n baseName: string;\n};\n\nexport async function downloadBinaryNoExtract(\n logger: MiLogger,\n baseDir: string,\n softwareName: string,\n tgzName: string,\n arch: string,\n platform: string,\n): Promise<DownloadBinaryResult> {\n const opts = getPathsForDownload(softwareName, tgzName, baseDir, newArch(arch), newOs(platform));\n const { archiveUrl, alternativeArchiveGAUrl, archivePath } = opts;\n\n try {\n await downloadArchive(logger, archiveUrl, archivePath);\n opts.wasDownloadedFrom = archiveUrl;\n } catch (e: unknown) {\n await downloadArchive(logger, alternativeArchiveGAUrl, archivePath);\n opts.wasDownloadedFrom = alternativeArchiveGAUrl;\n }\n\n return opts;\n}\n\nexport async function downloadBinary(\n logger: MiLogger,\n baseDir: string,\n softwareName: string,\n archiveName: string,\n arch: string,\n platform: string,\n): Promise<DownloadBinaryResult> {\n const opts = getPathsForDownload(softwareName, archiveName, baseDir, newArch(arch), newOs(platform));\n const { archiveUrl, alternativeArchiveGAUrl, archivePath, archiveType, targetFolder, baseName } = opts;\n\n try {\n await downloadArchive(logger, archiveUrl, archivePath);\n opts.wasDownloadedFrom = archiveUrl;\n } catch (e: unknown) {\n await downloadArchive(logger, alternativeArchiveGAUrl, archivePath);\n opts.wasDownloadedFrom = alternativeArchiveGAUrl;\n }\n\n await extractArchive(logger, archivePath, archiveType, targetFolder);\n\n return opts;\n}\n\nfunction getPathsForDownload(\n softwareName: string,\n archiveName: string,\n baseDir: string,\n arch: ArchType,\n os: OSType,\n): DownloadBinaryResult {\n const baseName = `${archiveName}-${arch}`;\n const archiveType = osToArchiveType[os];\n\n const archiveFileName = `${baseName}.${archiveType}`;\n const archiveUrl = `${cdn}/${softwareName}/${os}/${archiveFileName}`;\n const alternativeArchiveGAUrl = `${gaCdn}/${softwareName}/${os}/${archiveFileName}`;\n const archivePath = upath.join(baseDir, archiveFileName);\n // folder where binary distribution of pl will be unpacked\n const targetFolder = upath.join(baseDir, baseName);\n\n return {\n archiveUrl,\n alternativeArchiveGAUrl,\n archivePath,\n archiveType,\n targetFolder,\n baseName,\n };\n}\n\nexport type DownloadInfo = {\n dstArchive?: string;\n fileExisted?: boolean;\n dirnameCreated?: boolean;\n statusCode?: number;\n errorMsg?: string;\n tmpPath?: string;\n wroteTmp?: boolean;\n tmpExisted?: boolean;\n renamed?: boolean;\n newExisted?: boolean;\n};\n\nexport async function downloadArchive(\n logger: MiLogger, archiveUrl: string, dstArchiveFile: string,\n): Promise<DownloadInfo> {\n const state: DownloadInfo = {};\n state.dstArchive = dstArchiveFile;\n\n try {\n state.fileExisted = await fileExists(dstArchiveFile);\n if (state.fileExisted) {\n logger.info(`Platforma Backend archive download skipped: '${dstArchiveFile}' already exists`);\n return state;\n }\n\n await fsp.mkdir(upath.dirname(dstArchiveFile), { recursive: true });\n state.dirnameCreated = true;\n\n logger.info(`Downloading archive:\\n URL: ${archiveUrl}\\n Save to: ${dstArchiveFile}`);\n\n const { body, statusCode } = await request(archiveUrl);\n state.statusCode = statusCode;\n if (statusCode != 200) {\n // completely draining the stream to prevent leaving open connections\n const textBody = await text(body);\n state.errorMsg = `failed to download archive: ${statusCode}, response: ${textBody.slice(0, 1000)}`;\n logger.error(state.errorMsg);\n throw new Error(state.errorMsg);\n }\n\n // to prevent incomplete downloads we first write in a temp file\n state.tmpPath = dstArchiveFile + '.tmp';\n await Readable.toWeb(body).pipeTo(Writable.toWeb(fs.createWriteStream(state.tmpPath)));\n state.wroteTmp = true;\n state.tmpExisted = await fileExists(state.tmpPath);\n\n // and then atomically rename it\n await fsp.rename(state.tmpPath, dstArchiveFile);\n state.renamed = true;\n state.newExisted = await fileExists(dstArchiveFile);\n\n return state;\n } catch (e: unknown) {\n const msg = `downloadArchive: error ${JSON.stringify(e)} occurred, state: ${JSON.stringify(state)}`;\n logger.error(msg);\n throw new Error(msg);\n }\n}\n\n/** Used to prevent mid-way interrupted unarchived folders to be used */\nconst MarkerFileName = '.ok';\n\nexport async function extractArchive(\n logger: MiLogger,\n archivePath: string,\n archiveType: ArchiveType,\n dstFolder: string,\n) {\n logger.info('extracting archive...');\n logger.info(` archive path: '${archivePath}'`);\n logger.info(` target dir: '${dstFolder}'`);\n\n if (!(await fileExists(archivePath))) {\n const msg = `Platforma Backend binary archive not found at '${archivePath}'`;\n logger.error(msg);\n throw new Error(msg);\n }\n\n const markerFilePath = upath.join(dstFolder, MarkerFileName);\n\n if (await fileExists(markerFilePath)) {\n logger.info(`Platforma Backend binaries unpack skipped: '${dstFolder}' exists`);\n return;\n }\n\n if (await fileExists(dstFolder)) {\n logger.info(`Removing previous incompletely unpacked folder: '${dstFolder}'`);\n await fsp.rm(dstFolder, { recursive: true });\n }\n\n logger.info(` creating target dir '${dstFolder}'`);\n await fsp.mkdir(dstFolder, { recursive: true });\n\n logger.info(\n `Unpacking Platforma Backend archive:\\n Archive: ${archivePath}\\n Target dir: ${dstFolder}`,\n );\n\n switch (archiveType) {\n case 'tgz':\n await tar.x({\n file: archivePath,\n cwd: dstFolder,\n gzip: true,\n });\n break;\n\n case 'zip':\n await decompress(archivePath, dstFolder);\n break;\n\n default:\n assertNever(archiveType);\n }\n\n // writing marker file, to be able in the future detect that we completely unarchived the tar file\n await fsp.writeFile(markerFilePath, 'ok');\n\n logger.info(` ... unpack done.`);\n}\n\nexport type ArchiveType = 'tgz' | 'zip';\n\nconst osToArchiveType: Record<OSType, ArchiveType> = {\n linux: 'tgz',\n macos: 'tgz',\n windows: 'zip',\n};\n","declare const PL_VERSION: string;\n\nexport function getDefaultPlVersion(): string {\n return PL_VERSION;\n}\n","import type { MiLogger } from '@milaboratories/ts-helpers';\nimport { assertNever } from '@milaboratories/ts-helpers';\nimport { downloadBinary } from './pl_binary_download';\nimport { getDefaultPlVersion } from './pl_version';\nimport os from 'os';\nimport upath from 'upath';\nimport type { OSType } from './os_and_arch';\nimport { newOs } from './os_and_arch';\n\n/** Shows how the binary should be got. */\nexport type PlBinarySource = PlBinarySourceDownload | PlBinarySourceLocal;\n\nexport type PlBinarySourceDownload = {\n readonly type: 'Download';\n readonly version: string;\n};\n\nexport type PlBinarySourceLocal = {\n readonly type: 'Local';\n readonly path: string;\n};\n\nexport function newDefaultPlBinarySource(): PlBinarySourceDownload {\n return { type: 'Download', version: getDefaultPlVersion() };\n}\n\nexport async function resolveLocalPlBinaryPath(\n logger: MiLogger,\n downloadDir: string,\n src: PlBinarySource,\n): Promise<string> {\n switch (src.type) {\n case 'Download':\n const ops = await downloadBinary(logger, downloadDir, 'pl', `pl-${src.version}`, os.arch(), os.platform());\n return upath.join(ops.baseName, 'binaries', osToBinaryName[newOs(os.platform())]);\n\n case 'Local':\n return src.path;\n\n default:\n assertNever(src);\n }\n}\n\nexport const osToBinaryName: Record<OSType, string> = {\n linux: 'platforma',\n macos: 'platforma',\n windows: 'platforma.exe',\n};\n","import { fileExists } from '@milaboratories/ts-helpers';\nimport fs from 'fs/promises';\nimport upath from 'upath';\n\nexport function filePid(dir: string) {\n return upath.join(dir, 'pl_pid');\n}\n\nexport async function readPid(filePath: string): Promise<number | undefined> {\n if (!(await fileExists(filePath))) {\n return undefined;\n }\n\n const text = await fs.readFile(filePath);\n\n return Number(text.toString());\n}\n\nexport async function writePid(filePath: string, pid: number) {\n await fs.writeFile(filePath, JSON.stringify(pid));\n}\n","import type { MiLogger } from '@milaboratories/ts-helpers';\n\n/** Records all inputs and outputs of one's choice, so if the error happened\n * one can check how it was by just printing this structure. */\nexport type Trace = Record<string, any>;\n\nexport function newTrace(): Trace {\n return {};\n}\n\nexport function trace(t: Trace, k: string, v: any) {\n t[k] = v;\n return v;\n}\n\n/** Creates a trace and runs a function with it. The function can record all its\n * logs or traces using `trace` fn. */\nexport async function withTrace<T>(\n logger: MiLogger,\n fn: (trace: (k: string, v: any) => typeof v, t: Trace) => Promise<T>,\n): Promise<T> {\n const t = newTrace();\n try {\n const result = await fn((k: string, v: any) => trace(t, k, v), t);\n return result;\n } catch (e: any) {\n logger.error(`error ${e} while doing traced operation, state: ${JSON.stringify(t)}`);\n throw e;\n }\n}\n","import type {\n ProcessOptions } from './process';\nimport {\n isProcessAlive,\n processStop,\n processWaitStopped,\n processRun,\n} from './process';\nimport type { PlBinarySource } from '../common/pl_binary';\nimport { newDefaultPlBinarySource, resolveLocalPlBinaryPath } from '../common/pl_binary';\nimport type { MiLogger } from '@milaboratories/ts-helpers';\nimport { notEmpty } from '@milaboratories/ts-helpers';\nimport type { ChildProcess, SpawnOptions } from 'child_process';\nimport { filePid, readPid, writePid } from './pid';\nimport type { Trace } from './trace';\nimport { withTrace } from './trace';\nimport upath from 'upath';\nimport fsp from 'fs/promises';\nimport type { Required } from 'utility-types';\n\nexport const LocalConfigYaml = 'config-local.yaml';\n\n/**\n * Represents a local running pl-core,\n * and has methods to start, check if it's running, stop and wait for stopping it.\n * Also, a hook on pl-core closed can be provided.\n */\nexport class LocalPl {\n private instance?: ChildProcess;\n public pid?: number;\n private nRuns: number = 0;\n private lastRunHistory: Trace = {};\n private wasStopped = false;\n\n constructor(\n private readonly logger: MiLogger,\n private readonly workingDir: string,\n private readonly startOptions: ProcessOptions,\n private readonly initialStartHistory: Trace,\n private readonly onClose?: (pl: LocalPl) => Promise<void>,\n private readonly onError?: (pl: LocalPl) => Promise<void>,\n private readonly onCloseAndError?: (pl: LocalPl) => Promise<void>,\n private readonly onCloseAndErrorNoStop?: (pl: LocalPl) => Promise<void>,\n ) {}\n\n async start() {\n await withTrace(this.logger, async (trace, t) => {\n this.wasStopped = false;\n const instance = processRun(this.logger, this.startOptions);\n instance.on('error', (e: any) => {\n this.logger.error(\n `error '${e}', while running platforma, started opts: ${JSON.stringify(this.debugInfo())}`,\n );\n\n // keep in mind there are no awaits here, it will be asynchronous\n if (this.onError !== undefined) this.onError(this);\n if (this.onCloseAndError !== undefined) this.onCloseAndError(this);\n if (this.onCloseAndErrorNoStop !== undefined && !this.wasStopped)\n this.onCloseAndErrorNoStop(this);\n });\n instance.on('close', () => {\n this.logger.warn(`platforma was closed, started opts: ${JSON.stringify(this.debugInfo())}`);\n\n // keep in mind there are no awaits here, it will be asynchronous\n if (this.onClose !== undefined) this.onClose(this);\n if (this.onCloseAndError !== undefined) this.onCloseAndError(this);\n if (this.onCloseAndErrorNoStop !== undefined && !this.wasStopped)\n this.onCloseAndErrorNoStop(this);\n });\n\n trace('started', true);\n\n const pidFile = trace('pidFile', filePid(this.workingDir));\n trace('pid', notEmpty(instance.pid));\n trace('pidWritten', await writePid(pidFile, notEmpty(instance.pid)));\n\n this.nRuns++;\n this.instance = instance;\n this.pid = instance.pid;\n this.lastRunHistory = t;\n });\n }\n\n stop() {\n // TODO use this.instance to stop the process\n this.wasStopped = true;\n processStop(notEmpty(this.pid));\n }\n\n async waitStopped() {\n await processWaitStopped(notEmpty(this.pid), 15000);\n }\n\n stopped() {\n return this.wasStopped;\n }\n\n async isAlive(): Promise<boolean> {\n return await isProcessAlive(notEmpty(this.pid));\n }\n\n debugInfo() {\n return {\n lastRunHistory: this.lastRunHistory,\n nRuns: this.nRuns,\n pid: this.pid,\n workingDir: this.workingDir,\n initialStartHistory: this.initialStartHistory,\n wasStopped: this.wasStopped,\n };\n }\n}\n\n/** Options to start a local pl-core. */\nexport type LocalPlOptions = {\n /** From what directory start a process. */\n readonly workingDir: string;\n /** A string representation of yaml config. */\n readonly config: string;\n /** How to get a binary, download it or get an existing one (default: download latest version) */\n readonly plBinary?: PlBinarySource;\n /** Additional options for a process, environments, stdout, stderr etc. */\n readonly spawnOptions?: SpawnOptions;\n /**\n * If the previous pl-core was started from the same directory,\n * we can check if it's still running and then stop it before starting a new one.\n * (default: true)\n */\n readonly closeOld?: boolean;\n\n readonly onClose?: (pl: LocalPl) => Promise<void>;\n readonly onError?: (pl: LocalPl) => Promise<void>;\n readonly onCloseAndError?: (pl: LocalPl) => Promise<void>;\n readonly onCloseAndErrorNoStop?: (pl: LocalPl) => Promise<void>;\n};\n\ntype LocalPlOptionsFull = Required<LocalPlOptions, 'plBinary' | 'spawnOptions' | 'closeOld'>;\n\n/**\n * Starts pl-core, if the option was provided downloads a binary, reads license environments etc.\n */\nexport async function localPlatformaInit(logger: MiLogger, _ops: LocalPlOptions): Promise<LocalPl> {\n // filling-in default values\n const ops = {\n plBinary: newDefaultPlBinarySource(),\n spawnOptions: {},\n closeOld: true,\n ..._ops,\n } satisfies LocalPlOptionsFull;\n\n return await withTrace(logger, async (trace, t) => {\n trace('startOptions', { ...ops, config: 'too wordy' });\n\n const workDir = upath.resolve(ops.workingDir);\n\n if (ops.closeOld) {\n trace('closeOld', await localPlatformaReadPidAndStop(logger, workDir));\n }\n\n const configPath = upath.join(workDir, LocalConfigYaml);\n\n logger.info(`writing configuration '${configPath}'...`);\n await fsp.writeFile(configPath, ops.config);\n\n const plBinPath = upath.join(workDir, 'binaries');\n const baseBinaryPath = await resolveLocalPlBinaryPath(logger, plBinPath, ops.plBinary);\n\n const binaryPath = trace('binaryPath', upath.join('binaries', baseBinaryPath));\n\n const processOpts: ProcessOptions = {\n cmd: binaryPath,\n args: ['--config', configPath],\n opts: {\n env: { ...process.env },\n cwd: workDir,\n stdio: ['pipe', 'ignore', 'inherit'],\n windowsHide: true, // hide a terminal on Windows\n ...ops.spawnOptions,\n },\n };\n trace('processOpts', {\n cmd: processOpts.cmd,\n args: processOpts.args,\n cwd: processOpts.opts.cwd,\n });\n\n const pl = new LocalPl(\n logger,\n ops.workingDir,\n processOpts,\n t,\n ops.onClose,\n ops.onError,\n ops.onCloseAndError,\n ops.onCloseAndErrorNoStop,\n );\n await pl.start();\n\n return pl;\n });\n}\n\n/** Reads a pid of the old pl-core if it was started in the same working directory,\n * and closes it. */\nasync function localPlatformaReadPidAndStop(\n logger: MiLogger,\n workingDir: string,\n): Promise<Record<string, any>> {\n return await withTrace(logger, async (trace, t) => {\n const file = trace('pidFilePath', filePid(workingDir));\n\n const oldPid = trace('pid', await readPid(file));\n const alive = trace('wasAlive', await isProcessAlive(oldPid));\n\n if (oldPid !== undefined && alive) {\n trace('stopped', processStop(oldPid));\n trace('waitStopped', await processWaitStopped(oldPid, 10_000));\n }\n\n return t;\n });\n}\n","import type { ConnectConfig, ClientChannel, SFTPWrapper } from 'ssh2';\nimport ssh, { Client } from 'ssh2';\nimport net from 'net';\nimport dns from 'dns';\nimport fs from 'fs';\nimport { readFile } from 'fs/promises';\nimport upath from 'upath';\nimport { RetryablePromise, type MiLogger } from '@milaboratories/ts-helpers';\nimport { randomBytes } from 'crypto';\n\nconst defaultConfig: ConnectConfig = {\n keepaliveInterval: 60000,\n keepaliveCountMax: 10,\n};\n\nexport type SshAuthMethods = 'publickey' | 'password';\nexport type SshAuthMethodsResult = SshAuthMethods[];\nexport type SshDirContent = {\n files: string[];\n directories: string[];\n};\n\nexport class SshClient {\n private config?: ConnectConfig;\n public homeDir?: string;\n private forwardedServers: net.Server[] = [];\n\n constructor(\n private readonly logger: MiLogger,\n private readonly client: Client,\n ) {}\n\n /**\n * Initializes the SshClient and establishes a connection using the provided configuration.\n * @param config - The connection configuration object for the SSH client.\n * @returns A new instance of SshClient with an active connection.\n */\n public static async init(logger: MiLogger, config: ConnectConfig): Promise<SshClient> {\n const withDefaults = {\n ...defaultConfig,\n ...config,\n };\n\n const client = new SshClient(logger, new Client());\n await client.connect(withDefaults);\n\n return client;\n }\n\n public getForwardedServers() {\n return this.forwardedServers;\n }\n\n public getFullHostName() {\n return `${this.config?.host}:${this.config?.port}`;\n }\n\n public getUserName() {\n return this.config?.username;\n }\n\n /**\n * Connects to the SSH server using the specified configuration.\n * @param config - The connection configuration object for the SSH client.\n * @returns A promise that resolves when the connection is established or rejects on error.\n */\n public async connect(config: ConnectConfig) {\n this.config = config;\n return await connect(this.client, config, () => {}, () => {});\n }\n\n /**\n * Executes a command on the SSH server.\n * @param command - The command to execute on the remote server.\n * @returns A promise resolving with the command's stdout and stderr outputs.\n */\n public async exec(command: string): Promise<SshExecResult> {\n return new Promise((resolve, reject) => {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n this.client.exec(command, (err: any, stream: ClientChannel) => {\n if (err) {\n return reject(`ssh.exec: ${command}, error occurred: ${err}`);\n }\n\n let stdout = '';\n let stderr = '';\n\n stream.on('close', (code: number) => {\n if (code === 0) {\n resolve({ stdout, stderr });\n } else {\n reject(new Error(`Command ${command} exited with code ${code}`));\n }\n }).on('data', (data: ArrayBuffer) => {\n stdout += data.toString();\n }).stderr.on('data', (data: ArrayBuffer) => {\n stderr += data.toString();\n });\n });\n });\n }\n\n /**\n * Retrieves the supported authentication methods for a given host and port.\n * @param host - The hostname or IP address of the server.\n * @param port - The port number to connect to on the server.\n * @returns 'publickey' | 'password'[] A promise resolving with a list of supported authentication methods.\n */\n public static async getAuthTypes(host: string, port: number): Promise<SshAuthMethodsResult> {\n return new Promise((resolve) => {\n let stdout = '';\n const conn = new Client();\n\n conn.on('ready', () => {\n conn.end();\n const types = this.extractAuthMethods(stdout);\n resolve(types.length === 0 ? ['publickey', 'password'] : types as SshAuthMethodsResult);\n });\n\n conn.on('error', () => {\n conn.end();\n resolve(['publickey', 'password']);\n });\n\n conn.connect({\n host,\n port,\n username: new Date().getTime().toString(),\n debug: (err) => {\n stdout += `${err}\\n`;\n },\n });\n });\n }\n\n /**\n * Extracts authentication methods from debug logs.\n * @param log - The debug log output containing authentication information.\n * @returns An array of extracted authentication methods.\n */\n private static extractAuthMethods(log: string): string[] {\n const match = log.match(/Inbound: Received USERAUTH_FAILURE \\((.+)\\)/);\n return match && match[1] ? match[1].split(',').map((method) => method.trim()) : [];\n }\n\n /**\n * Sets up port forwarding between a remote port on the SSH server and a local port.\n * A new connection is used for this operation instead of an existing one.\n * @param ports - An object specifying the remote and local port configuration.\n * @param config - Optional connection configuration for the SSH client.\n * @returns { server: net.Server } A promise resolving with the created server instance.\n */\n public async forwardPort(ports: { remotePort: number; localPort: number; localHost?: string }, config?: ConnectConfig): Promise<{ server: net.Server }> {\n const log = `ssh.forward:${ports.localPort}:${ports.remotePort}.id_${randomBytes(1).toString('hex')}`;\n config = config ?? this.config;\n\n // we make this thing persistent so that if the connection\n // drops (it happened in the past because of lots of errors and forwardOut opened channels),\n // we'll recreate it here.\n const persistentClient = new RetryablePromise((p: RetryablePromise<Client>) => {\n return new Promise<Client>((resolve, reject) => {\n const client = new Client();\n\n client.on('ready', () => {\n this.logger.info(`${log}.client.ready`);\n resolve(client);\n });\n\n client.on('error', (err) => {\n this.logger.info(`${log}.client.error: ${err}`);\n p.reset();\n reject(err);\n });\n\n client.on('close', () => {\n this.logger.info(`${log}.client.closed`);\n p.reset();\n });\n\n client.connect(config!);\n });\n });\n\n await persistentClient.ensure(); // warm up a connection\n\n return new Promise((resolve, reject) => {\n const server = net.createServer({ pauseOnConnect: true }, async (localSocket) => {\n const sockLog = `${log}.sock_${randomBytes(1).toString('hex')}`;\n // this.logger.info(`${sockLog}.localSocket: start connection`);\n let conn: Client;\n try {\n conn = await persistentClient.ensure();\n } catch (e: unknown) {\n this.logger.info(`${sockLog}.persistentClient.catch: ${e}`);\n localSocket.end();\n return;\n }\n\n let stream: ClientChannel;\n try {\n stream = await forwardOut(this.logger, conn, '127.0.0.1', 0, '127.0.0.1', ports.remotePort);\n } catch (e: unknown) {\n this.logger.error(`${sockLog}.forwardOut.err: ${e}`);\n localSocket.end();\n return;\n }\n\n localSocket.pipe(stream);\n stream.pipe(localSocket);\n localSocket.resume();\n // this.logger.info(`${sockLog}.forwardOut: connected`);\n\n stream.on('error', (err: unknown) => {\n this.logger.error(`${sockLog}.stream.error: ${err}`);\n localSocket.end();\n stream.end();\n });\n stream.on('close', () => {\n // this.logger.info(`${sockLog}.stream.close: closed`);\n localSocket.end();\n stream.end();\n });\n localSocket.on('close', () => {\n this.logger.info(`${sockLog}.localSocket: closed`);\n localSocket.end();\n stream.end();\n });\n });\n\n server.listen(ports.localPort, '127.0.0.1', () => {\n this.logger.info(`${log}.server: started listening`);\n this.forwardedServers.push(server);\n resolve({ server });\n });\n\n server.on('error', (err) => {\n server.close();\n reject(new Error(`${log}.server: error: ${JSON.stringify(err)}`));\n });\n\n server.on('close', () => {\n this.logger.info(`${log}.server: closed ${JSON.stringify(ports)}`);\n this.forwardedServers = this.forwardedServers.filter((s) => s !== server);\n });\n });\n }\n\n public closeForwardedPorts(): void {\n this.logger.info('[SSH] Closing all forwarded ports...');\n this.forwardedServers.forEach((server) => {\n const rawAddress = server.address();\n if (rawAddress && typeof rawAddress !== 'string') {\n const address: net.AddressInfo = rawAddress;\n this.logger.info(`[SSH] Closing port forward for server ${address.address}:${address.port}`);\n }\n\n server.close();\n });\n this.forwardedServers = [];\n }\n\n /**\n * Checks if a specified host is available by performing a DNS lookup.\n * @param hostname - The hostname or IP address to check.\n * @returns A promise resolving with `true` if the host is reachable, otherwise `false`.\n */\n public static async checkHostAvailability(hostname: string): Promise<boolean> {\n return new Promise((resolve) => {\n dns.lookup(hostname, (err) => {\n resolve(!err);\n });\n });\n }\n\n /**\n * Determines whether a private key requires a passphrase for use.\n * @param privateKey - The private key content to check.\n * @returns A promise resolving with `true` if a passphrase is required, otherwise `false`.\n */\n public static async isPassphraseRequiredForKey(privateKey: string): Promise<boolean> {\n return new Promise((resolve, reject) => {\n try {\n const keyOrError = ssh.utils.parseKey(privateKey);\n if (keyOrError instanceof Error) {\n resolve(true);\n }\n return resolve(false);\n } catch (err: unknown) {\n console.log('Error parsing privateKey');\n reject(new Error(`ssh.isPassphraseRequiredForKey: err ${err}`));\n }\n });\n }\n\n /**\n * Uploads a local file to a remote server via SFTP.\n * This function creates new SFTP connection\n * @param localPath - The local file path.\n * @param remotePath - The remote file path on the server.\n * @returns A promise resolving with `true` if the file was successfully uploaded.\n */\n public async uploadFile(localPath: string, remotePath: string): Promise<boolean> {\n return await this.withSftp(async (sftp) => {\n return new Promise((resolve, reject) => {\n sftp.fastPut(localPath, remotePath, (err) => {\n if (err) {\n const newErr = new Error(\n `ssh.uploadFile: err: ${err}, localPath: ${localPath}, remotePath: ${remotePath}`);\n return reject(newErr);\n }\n resolve(true);\n });\n });\n });\n }\n\n public async withSftp<R>(callback: (sftp: SFTPWrapper) => Promise<R>): Promise<R> {\n return new Promise((resolve, reject) => {\n this.client.sftp((err, sftp) => {\n if (err) {\n return reject(new Error(`ssh.withSftp: sftp err: ${err}`));\n }\n\n callback(sftp)\n .then(resolve)\n .catch((err) => {\n reject(new Error(`ssh.withSftp.callback: err ${err}`));\n })\n .finally(() => {\n sftp?.end();\n });\n });\n });\n }\n\n public async writeFileOnTheServer(remotePath: string, data: string | Buffer, mode: number = 0o660) {\n return this.withSftp(async (sftp) => {\n return this.writeFile(sftp, remotePath, data, mode);\n });\n }\n\n public async getForderStructure(sftp: SFTPWrapper, remotePath: string, data: SshDirContent = { files: [], directories: [] }): Promise<SshDirContent> {\n return new Promise((resolve, reject) => {\n sftp.readdir(remotePath, async (err, items) => {\n if (err) {\n return reject(err);\n }\n\n for (const item of items) {\n const itemPath = `${remotePath}/${item.filename}`;\n if (item.attrs.isDirectory()) {\n data.directories.push(itemPath);\n try {\n await this.getForderStructure(sftp, itemPath, data);\n } catch (error) {\n return reject(error);\n }\n } else {\n data.files.push(itemPath);\n }\n }\n resolve(data);\n });\n });\n }\n\n public rmdir(sftp: SFTPWrapper, path: string) {\n return new Promise((resolve, reject) => {\n sftp.rmdir(path, (err) => err ? reject(err) : resolve(true));\n });\n }\n\n public unlink(sftp: SFTPWrapper, path: string) {\n return new Promise((resolve, reject) => {\n sftp.unlink(path, (err) => err ? reject(err) : resolve(true));\n });\n }\n\n public async deleteFolder(path: string) {\n return this.withSftp(async (sftp) => {\n try {\n const list = await this.getForderStructure(sftp, path);\n this.logger.info(`ssh.deleteFolder list of files and directories`);\n this.logger.info(`ssh.deleteFolder list of files: ${list.files}`);\n this.logger.info(`ssh.deleteFolder list of directories: ${list.directories}`);\n\n for (const filePath of list.files) {\n this.logger.info(`ssh.deleteFolder unlink file ${filePath}`);\n await this.unlink(sftp, filePath);\n }\n\n list.directories.sort((a, b) => b.length - a.length);\n\n for (const directoryPath of list.directories) {\n this.logger.info(`ssh.deleteFolder rmdir ${directoryPath}`);\n await this.rmdir(sftp, directoryPath);\n }\n\n await this.rmdir(sftp, path);\n return true;\n } catch (e: unknown) {\n this.logger.error(e);\n const message = e instanceof Error ? e.message : '';\n throw new Error(`ssh.deleteFolder: path: ${path}, message: ${message}`);\n }\n });\n }\n\n public async readFile(remotePath: string): Promise<string> {\n return this.withSftp(async (sftp) => {\n return new Promise((resolve, reject) => {\n sftp.readFile(remotePath, (err, buffer) => {\n if (err) {\n return reject(new Error(`ssh.readFile: err occurred ${err}`));\n }\n resolve(buffer.toString());\n });\n });\n });\n }\n\n async chmod(path: string, mode: number) {\n return this.withSftp(async (sftp) => {\n return new Promise((resolve, reject) => {\n sftp.chmod(path, mode, (err) => {\n if (err) {\n return reject(new Error(`ssh.chmod: ${err}, path: ${path}, mode: ${mode}`));\n }\n return resolve(undefined);\n });\n });\n });\n }\n\n async checkFileExists(remotePath: string) {\n return this.withSftp(async (sftp) => {\n return new Promise((resolve, reject) => {\n sftp.stat(remotePath, (err: Error | undefined, stats) => {\n if (err) {\n if ((err as unknown as { code?: number })?.code === 2) {\n return resolve(false);\n }\n return reject(new Error(`ssh.checkFileExists: err ${err}`));\n }\n resolve(stats.isFile());\n });\n });\n });\n }\n\n async checkPathExists(remotePath: string): Promise<{ exists: boolean; isFile: boolean; isDirectory: boolean }> {\n return this.withSftp(async (sftp) => {\n return new Promise((resolve, reject) => {\n sftp.stat(remotePath, (err, stats) => {\n if (err) {\n if ((err as Error & { code: number }).code === 2) {\n return resolve({ exists: false, isFile: false, isDirectory: false });\n }\n return reject(new Error(`ssh.checkPathExists: ${err}`));\n }\n resolve({\n exists: true,\n isFile: stats.isFile(),\n isDirectory: stats.isDirectory(),\n });\n });\n });\n });\n }\n\n private async writeFile(sftp: SFTPWrapper, remotePath: string, data: string | Buffer, mode: number = 0o660): Promise<boolean> {\n return new Promise((resolve, reject) => {\n sftp.writeFile(remotePath, data, { mode }, (err) => {\n if (err) {\n return reject(new Error(`ssh.writeFile: err ${err}, remotePath: ${remotePath}`));\n }\n resolve(true);\n });\n });\n }\n\n public uploadFileUsingExistingSftp(sftp: SFTPWrapper, localPath: string, remotePath: string, mode: number = 0o660) {\n return new Promise((resolve, reject) => {\n readFile(localPath).then(async (result: Buffer) => {\n this.writeFile(sftp, remotePath, result, mode)\n .then(() => {\n resolve(undefined);\n })\n .catch((err) => {\n const msg = `uploadFileUsingExistingSftp: error ${err} occurred`;\n this.logger.error(msg);\n reject(new Error(msg));\n });\n });\n });\n }\n\n private async __uploadDirectory(sftp: SFTPWrapper, localDir: string, remoteDir: string, mode: number = 0o660): Promise<void> {\n return new Promise((resolve, reject) => {\n fs.readdir(localDir, async (err, files) => {\n if (err) {\n return reject(new Error(`ssh.__uploadDir: err ${err}, localDir: ${localDir}, remoteDir: ${remoteDir}`));\n }\n\n try {\n await this.__createRemoteDirectory(sftp, remoteDir);\n for (const file of files) {\n const localPath = upath.join(localDir, file);\n const remotePath = `${remoteDir}/${file}`;\n\n if (fs.lstatSync(localPath).isDirectory()) {\n await this.__uploadDirectory(sftp, localPath, remotePath, mode);\n } else {\n await this.uploadFileUsingExistingSftp(sftp, localPath, remotePath, mode);\n }\n }\n\n resolve();\n } catch (err) {\n const msg = `ssh.__uploadDir: catched err ${err}`;\n this.logger.error(msg);\n reject(new Error(msg));\n }\n });\n });\n }\n\n /**\n * Uploads a local directory and its contents (including subdirectories) to the remote server via SFTP.\n * @param localDir - The path to the local directory to upload.\n * @param remoteDir - The path to the remote directory on the server.\n * @returns A promise that resolves when the directory and its contents are uploaded.\n */\n public async uploadDirectory(localDir: string, remoteDir: string, mode: number = 0o660): Promise<void> {\n return new Promise((resolve, reject) => {\n this.withSftp(async (sftp: SFTPWrapper) => {\n try {\n await this.__uploadDirectory(sftp, localDir, remoteDir, mode);\n resolve();\n } catch (e: unknown) {\n reject(new Error(`ssh.uploadDirectory: ${e}`));\n }\n });\n });\n }\n\n /**\n * Ensures that a remote directory and all its parent directories exist.\n * @param sftp - The SFTP wrapper.\n * @param remotePath - The path to the remote directory.\n * @returns A promise that resolves when the directory is created.\n */\n private __createRemoteDirectory(sftp: SFTPWrapper, remotePath: string): Promise<void> {\n return new Promise((resolve, reject) => {\n const directories = remotePath.split('/');\n let currentPath = '';\n\n const createNext = (index: number) => {\n if (index >= directories.length) {\n return resolve();\n }\n\n currentPath += `${directories[index]}/`;\n\n sftp.stat(currentPath, (err) => {\n if (err) {\n sftp.mkdir(currentPath, (err) => {\n if (err) {\n return reject(new Error(`ssh.__createRemDir: err ${err}, remotePath: ${remotePath}`));\n }\n createNext(index + 1);\n });\n } else {\n createNext(index + 1);\n }\n });\n };\n\n createNext(0);\n });\n }\n\n /**\n * Ensures that a remote directory and all its parent directories exist.\n * @param sftp - The SFTP wrapper.\n * @param remotePath - The path to the remote directory.\n * @returns A promise that resolves when the directory is created.\n */\n public ensureRemoteDirCreated(remotePath: string, mode: number = 0o755): Promise<void> {\n return this.withSftp(async (sftp) => {\n const directories = remotePath.split('/');\n let currentPath = '';\n\n for (const directory of directories) {\n currentPath += `${directory}/`;\n\n try {\n await new Promise<void>((resolve, reject) => {\n sftp.stat(currentPath, (err) => {\n if (!err) return resolve();\n\n sftp.mkdir(currentPath, { mode }, (err) => {\n if (err) {\n return reject(new Error(`ssh.createRemoteDir: err ${err}, remotePath: ${remotePath}`));\n }\n resolve();\n });\n });\n });\n } catch (error) {\n console.error(`Failed to create directory: ${currentPath}`, error);\n throw error;\n }\n }\n });\n }\n\n /**\n * Downloads a file from the remote server to a local path via SFTP.\n * @param remotePath - The remote file path on the server.\n * @param localPath - The local file path to save the file.\n * @returns A promise resolving with `true` if the file was successfully downloaded.\n */\n public async downloadFile(remotePath: string, localPath: string): Promise<boolean> {\n return this.withSftp(async (sftp) => {\n return new Promise((resolve, reject) => {\n sftp.fastGet(remotePath, localPath, (err) => {\n if (err) {\n return reject(new Error(`ssh.downloadFile: err ${err}, remotePath: ${remotePath}, localPath: ${localPath}`));\n }\n resolve(true);\n });\n });\n });\n }\n\n /**\n * Closes the SSH client connection and forwarded ports.\n */\n public close(): void {\n this.closeForwardedPorts();\n this.client.end();\n }\n}\n\nexport type SshExecResult = { stdout: string; stderr: string };\n\nasync function connect(\n client: Client,\n config: ConnectConfig,\n onError: (e: unknown) => void,\n onClose: () => void,\n): Promise<Client> {\n return new Promise((resolve, reject) => {\n client.on('ready', () => {\n resolve(client);\n });\n\n client.on('error', (err: unknown) => {\n onError(err);\n reject(new Error(`ssh.connect: error occurred: ${err}`));\n });\n\n client.on('close', () => {\n onClose();\n });\n\n client.connect(config);\n });\n}\n\nasync function forwardOut(logger: MiLogger, conn: Client, localHost: string, localPort: number, remoteHost: string, remotePort: number): Promise<ClientChannel> {\n return new Promise((resolve, reject) => {\n conn.forwardOut(localHost, localPort, remoteHost, remotePort, (err, stream) => {\n if (err) {\n logger.error(`forwardOut.error: ${err}`);\n return reject(err);\n }\n\n return resolve(stream);\n });\n });\n}\n","/** Just a lot of hardcoded paths of our current ssh deployment. */\n\nimport upath from 'upath';\nimport { newArch } from '../common/os_and_arch';\nimport { getDefaultPlVersion } from '../common/pl_version';\n\nexport const minioDirName = 'minio-2024-12-18T13-15-44Z';\nexport const supervisordDirName = 'supervisord-0.7.3';\nexport const supervisordSubDirName = 'supervisord_0.7.3_Linux_64-bit';\n\nexport function workDir(remoteHome: string) {\n return upath.join(remoteHome, '.platforma_ssh');\n}\n\nexport function binariesDir(remoteHome: string) {\n return upath.join(workDir(remoteHome), 'binaries');\n}\n\nexport function platformaBaseDir(remoteHome: string, arch: string) {\n return upath.join(binariesDir(remoteHome), `pl-${getDefaultPlVersion()}-${newArch(arch)}`);\n}\n\nexport function platformaDir(remoteHome: string, arch: string) {\n return upath.join(platformaBaseDir(remoteHome, arch), 'binaries');\n}\n\nexport function platformaBin(remoteHome: string, arch: string): string {\n return upath.join(platformaDir(remoteHome, arch), 'platforma');\n}\n\nexport function platformaConf(remoteHome: string): string {\n return upath.join(workDir(remoteHome), 'config.yaml');\n}\n\nexport function platformaFreePortBin(remoteHome: string, arch: string): string {\n return upath.join(platformaDir(remoteHome, arch), 'free-port');\n}\n\nexport function minioDir(remoteHome: string, arch: string) {\n return upath.join(binariesDir(remoteHome), `minio-2024-12-18T13-15-44Z-${newArch(arch)}`);\n}\n\nexport function minioBin(remoteHome: string, arch: string) {\n return upath.join(minioDir(remoteHome, arch), 'minio');\n}\n\nexport function supervisorBinDir(remoteHome: string, arch: string) {\n return upath.join(binariesDir(remoteHome), `supervisord-0.7.3-${newArch(arch)}`, supervisordSubDirName);\n}\n\nexport function supervisorBin(remoteHome: string, arch: string): string {\n return upath.join(supervisorBinDir(remoteHome, arch), 'supervisord');\n}\n\nexport function supervisorConf(remoteHome: string) {\n return upath.join(workDir(remoteHome), 'supervisor.conf');\n}\n\nexport function connectionInfo(remoteHome: string) {\n return upath.join(workDir(remoteHome), `connection.txt`);\n}\n","/** Provides helper functions to work with supervisord */\n\nimport type { MiLogger } from '@milaboratories/ts-helpers';\nimport * as plpath from './pl_paths';\nimport type { SshClient, SshExecResult } from './ssh';\nimport { randomBytes } from 'crypto';\n\nexport async function supervisorCtlStart(\n sshClient: SshClient,\n remoteHome: string, arch: string,\n) {\n const result = await supervisorExec(sshClient, remoteHome, arch, '--daemon');\n\n if (result.stderr) {\n throw new Error(`Can not run ssh Platforma ${result.stderr}`);\n }\n}\n\nexport async function supervisorStop(\n sshClient: SshClient,\n remoteHome: string, arch: string,\n) {\n const result = await supervisorExec(sshClient, remoteHome, arch, 'ctl shutdown');\n\n if (result.stderr) {\n throw new Error(`Can not stop ssh Platforma ${result.stderr}`);\n }\n}\n\n/** Provides a simple true/false response got from supervisord status\n * along with a debug info that could be showed in error logs (raw response from the command, parsed response etc). */\nexport type SupervisorStatus = {\n platforma?: boolean;\n minio?: boolean;\n allAlive: boolean; // true when both pl and minio are alive.\n rawResult?: SshExecResult;\n execError?: string;\n};\n\nexport async function supervisorStatus(\n logger: MiLogger,\n sshClient: SshClient,\n remoteHome: string, arch: string,\n): Promise<SupervisorStatus> {\n let result: SshExecResult;\n try {\n result = await supervisorExec(sshClient, remoteHome, arch, 'ctl status');\n } catch (e: unknown) {\n return { execError: String(e), allAlive: false };\n }\n\n if (result.stderr) {\n logger.info(`supervisord ctl status: stderr occurred: ${result.stderr}, stdout: ${result.stdout}`);\n\n return { rawResult: result, allAlive: false };\n }\n\n const platforma = isProgramRunning(result.stdout, 'platforma');\n const minio = isProgramRunning(result.stdout, 'minio');\n const status: SupervisorStatus = {\n rawResult: result,\n platforma,\n minio,\n allAlive: platforma && minio,\n };\n\n if (status.allAlive) {\n return status;\n }\n\n if (!status.minio) {\n logger.warn('Minio is not running on the server');\n }\n\n if (!status.platforma) {\n logger.warn('Platforma is not running on the server');\n }\n\n return status;\n}\n\nexport function generateSupervisordConfig(\n minioStorageDir: string,\n minioEnvs: Record<string, string>,\n supervisorRemotePort: number,\n remoteWorkDir: string,\n platformaConfigPath: string,\n\n minioPath: string,\n plPath: string,\n) {\n const minioEnvStr = Object.entries(minioEnvs).map(([key, value]) => `${key}=\"${value}\"`).join(',');\n const password = randomBytes(16).toString('hex');\n const freePort = supervisorRemotePort;\n\n return `\n[supervisord]\nlogfile=${remoteWorkDir}/supervisord.log\nloglevel=info\npidfile=${remoteWorkDir}/supervisord.pid\n\n[inet_http_server]\nport=127.0.0.1:${freePort}\nusername=default-user\npassword=${password}\n\n[supervisorctl]\nserverurl=http://127.0.0.1:${freePort}\nusername=default-user\npassword=${password}\n\n[program:platforma]\nautostart=true\ndepends_on=minio\ncommand=${plPath} --config ${platformaConfigPath}\ndirectory=${remoteWorkDir}\nautorestart=true\n\n[program:minio]\nautostart=true\nenvironment=${minioEnvStr}\ncommand=${minioPath} server ${minioStorageDir}\ndirectory=${remoteWorkDir}\nautorestart=true\n`;\n}\n\nexport async function supervisorExec(\n sshClient: SshClient,\n remoteHome: string, arch: string,\n command: string,\n) {\n const supervisorCmd = plpath.supervisorBin(remoteHome, arch);\n const supervisorConf = plpath.supervisorConf(remoteHome);\n\n const cmd = `${supervisorCmd} --configuration ${supervisorConf} ${command}`;\n return await sshClient.exec(cmd);\n}\n\nfunction isProgramRunning(output: string, programName: string) {\n // eslint-disable-next-line no-control-regex\n const stripAnsi = (str: string) => str.replace(/\\x1B\\[[0-9;]*m/g, '');\n\n const cleanedOutput = stripAnsi(output);\n\n return cleanedOutput.split('\\n').some((line) => {\n const [name, status] = line.trim().split(/\\s{2,}/); // Split string by 2 spaces.\n\n return name === programName && status === 'Running';\n });\n}\n","/** We store all info about the connection on the server,\n * so that another client could read the file and connect from another machine. */\nimport { z } from 'zod';\n\n//\n// Types\n//\n\nexport const PortPair = z.object({\n local: z.number(),\n remote: z.number(),\n});\n/** The pair of ports for forwarding. */\nexport type PortPair = z.infer<typeof PortPair>;\n\nexport const SshPlPorts = z.object({\n grpc: PortPair,\n monitoring: PortPair,\n debug: PortPair,\n minioPort: PortPair,\n minioConsolePort: PortPair,\n});\n/** All info about ports that are forwarded. */\nexport type SshPlPorts = z.infer<typeof SshPlPorts>;\n\nexport const ConnectionInfo = z.object({\n plUser: z.string(),\n plPassword: z.string(),\n ports: SshPlPorts,\n\n // It's false by default because it was added later,\n // and in some deployments there won't be useGlobalAccess flag in the file.\n useGlobalAccess: z.boolean().default(false),\n\n // We added the field afterwards, the pl backend was this version.\n plVersion: z.string().default('1.18.3'),\n});\n/** The content of the file that holds all the info about the connection on the remote server. */\nexport type ConnectionInfo = z.infer<typeof ConnectionInfo>;\n\n//\n// Funcs\n//\n\nexport function newConnectionInfo(\n plUser: string,\n plPassword: string,\n ports: SshPlPorts,\n useGlobalAccess: boolean,\n plVersion: string,\n): ConnectionInfo {\n return {\n plUser,\n plPassword,\n ports,\n useGlobalAccess,\n plVersion,\n };\n}\n\nexport function parseConnectionInfo(content: string): ConnectionInfo {\n return ConnectionInfo.parse(JSON.parse(content));\n}\n\nexport function stringifyConnectionInfo(conn: ConnectionInfo): string {\n return JSON.stringify(conn, undefined, 2);\n}\n","import type * as ssh from 'ssh2';\nimport { SshClient } from './ssh';\nimport type { MiLogger } from '@milaboratories/ts-helpers';\nimport { sleep, notEmpty } from '@milaboratories/ts-helpers';\nimport type { DownloadBinaryResult } from '../common/pl_binary_download';\nimport { downloadBinaryNoExtract } from '../common/pl_binary_download';\nimport upath from 'upath';\nimport * as plpath from './pl_paths';\nimport { getDefaultPlVersion } from '../common/pl_version';\n\nimport net from 'net';\nimport type { PlLicenseMode, SshPlConfigGenerationResult } from '@milaboratories/pl-config';\nimport { generateSshPlConfigs, getFreePort } from '@milaboratories/pl-config';\nimport type { SupervisorStatus } from './supervisord';\nimport { supervisorStatus, supervisorStop as supervisorCtlShutdown, generateSupervisordConfig, supervisorCtlStart } from './supervisord';\nimport type { ConnectionInfo, SshPlPorts } from './connection_info';\nimport { newConnectionInfo, parseConnectionInfo, stringifyConnectionInfo } from './connection_info';\nimport type { PlBinarySourceDownload } from '../common/pl_binary';\n\nexport class SshPl {\n private initState: PlatformaInitState = {};\n constructor(\n public readonly logger: MiLogger,\n public readonly sshClient: SshClient,\n private readonly username: string,\n ) { }\n\n public info() {\n return {\n username: this.username,\n initState: this.initState,\n };\n }\n\n public static async init(logger: MiLogger, config: ssh.ConnectConfig): Promise<SshPl> {\n try {\n const sshClient = await SshClient.init(logger, config);\n return new SshPl(logger, sshClient, notEmpty(config.username));\n } catch (e: unknown) {\n logger.error(`Connection error in SshClient.init: ${e}`);\n throw e;\n }\n }\n\n public cleanUp() {\n this.sshClient.close();\n }\n\n /** Provides an info if the platforma and minio are running along with the debug info. */\n public async isAlive(): Promise<SupervisorStatus> {\n const arch = await this.getArch();\n const remoteHome = await this.getUserHomeDirectory();\n return await supervisorStatus(this.logger, this.sshClient, remoteHome, arch.arch);\n }\n\n /** Starts all the services on the server.\n * Idempotent semantic: we could call it several times. */\n public async start() {\n const arch = await this.getArch();\n const remoteHome = await this.getUserHomeDirectory();\n\n try {\n if (!(await this.isAlive()).allAlive) {\n await supervisorCtlStart(this.sshClient, remoteHome, arch.arch);\n\n // We are waiting for Platforma to run to ensure that it has started.\n return await this.checkIsAliveWithInterval();\n }\n } catch (e: unknown) {\n const msg = `SshPl.start: error occurred ${e}`;\n this.logger.error(msg);\n throw new Error(msg);\n }\n }\n\n /** Stops all the services on the server.\n * Idempotent semantic: we could call it several times. */\n public async stop() {\n const arch = await this.getArch();\n const remoteHome = await this.getUserHomeDirectory();\n\n try {\n if ((await this.isAlive()).allAlive) {\n await supervisorCtlShutdown(this.sshClient, remoteHome, arch.arch);\n return await this.checkIsAliveWithInterval(undefined, undefined, false);\n }\n } catch (e: unknown) {\n const msg = `PlSsh.stop: error occurred ${e}`;\n this.logger.error(msg);\n throw new Error(msg);\n }\n }\n\n /** Stops the services, deletes a directory with the state and closes SSH connection. */\n public async reset(): Promise<boolean> {\n await this.stopAndClean();\n this.cleanUp();\n return true;\n }\n\n /** Stops platforma and deletes its state. */\n public async stopAndClean(): Promise<void> {\n const remoteHome = await this.getUserHomeDirectory();\n\n this.logger.info(`pl.reset: Stop Platforma on the server`);\n await this.stop();\n\n this.logger.info(`pl.reset: Deleting Platforma workDir ${plpath.workDir(remoteHome)} on the server`);\n await this.sshClient.deleteFolder(plpath.workDir(remoteHome));\n }\n\n /** Downloads binaries and untar them on the server,\n * generates all the configs, creates necessary dirs,\n * and finally starts all the services. */\n public async platformaInit(options: SshPlConfig): Promise<ConnectionInfo> {\n const state: PlatformaInitState = { localWorkdir: options.localWorkdir };\n\n try {\n // merge options with default ops.\n const ops: SshPlConfig = {\n ...defaultSshPlConfig,\n ...options,\n };\n state.plBinaryOps = ops.plBinary;\n state.arch = await this.getArch();\n state.remoteHome = await this.getUserHomeDirectory();\n state.alive = await this.isAlive();\n\n if (state.alive.allAlive) {\n state.userCredentials = await this.getUserCredentials(state.remoteHome);\n if (!state.userCredentials) {\n throw new Error(`SshPl.platformaInit: platforma is alive but userCredentials are not found`);\n }\n const sameGA = state.userCredentials.useGlobalAccess == ops.useGlobalAccess;\n const samePlVersion = state.userCredentials.plVersion == ops.plBinary!.version;\n state.needRestart = !(sameGA && samePlVersion);\n this.logger.info(`SshPl.platformaInit: need restart? ${state.needRestart}`);\n\n if (!state.needRestart)\n return state.userCredentials;\n\n await this.stop();\n }\n\n const downloadRes = await this.downloadBinariesAndUploadToTheServer(\n ops.localWorkdir, ops.plBinary!, state.remoteHome, state.arch,\n );\n state.binPaths = { ...downloadRes, history: undefined };\n state.downloadedBinaries = downloadRes.history;\n\n state.ports = await this.fetchPorts(state.remoteHome, state.arch);\n\n if (!state.ports.debug.remote || !state.ports.grpc.remote || !state.ports.minioPort.remote || !state.ports.minioConsolePort.remote || !state.ports.monitoring.remote) {\n throw new Error(`SshPl.platformaInit: remote ports are not defined`);\n }\n\n const config = await generateSshPlConfigs({\n logger: this.logger,\n workingDir: plpath.workDir(state.remoteHome),\n portsMode: {\n type: 'customWithMinio',\n ports: {\n debug: state.ports.debug.remote,\n grpc: state.ports.grpc.remote,\n minio: state.ports.minioPort.remote,\n minioConsole: state.ports.minioConsolePort.remote,\n monitoring: state.ports.monitoring.remote,\n\n grpcLocal: state.ports.grpc.local,\n minioLocal: state.ports.minioPort.local,\n },\n },\n licenseMode: ops.license,\n useGlobalAccess: notEmpty(ops.useGlobalAccess),\n });\n state.generatedConfig = { ...config, filesToCreate: { skipped: 'it is too wordy' } };\n\n for (const [filePath, content] of Object.entries(config.filesToCreate)) {\n await this.sshClient.writeFileOnTheServer(filePath, content);\n this.logger.info(`Created file ${filePath}`);\n }\n\n for (const dir of config.dirsToCreate) {\n await this.sshClient.ensureRemoteDirCreated(dir);\n this.logger.info(`Created directory ${dir}`);\n }\n\n const supervisorConfig = generateSupervisordConfig(\n config.minioConfig.storageDir,\n config.minioConfig.envs,\n await this.getFreePortForPlatformaOnServer(state.remoteHome, state.arch),\n config.workingDir,\n config.plConfig.configPath,\n state.binPaths.minioRelPath,\n state.binPaths.downloadedPl,\n );\n\n const writeResult = await this.sshClient.writeFileOnTheServer(plpath.supervisorConf(state.remoteHome), supervisorConfig);\n if (!writeResult) {\n throw new Error(`Can not write supervisord config on the server ${plpath.workDir(state.remoteHome)}`);\n }\n\n state.connectionInfo = newConnectionInfo(\n config.plUser,\n config.plPassword,\n state.ports,\n notEmpty(ops.useGlobalAccess),\n ops.plBinary!.version,\n );\n await this.sshClient.writeFileOnTheServer(\n plpath.connectionInfo(state.remoteHome),\n stringifyConnectionInfo(state.connectionInfo),\n );\n\n await this.start();\n state.started = true;\n this.initState = state;\n\n return state.connectionInfo;\n } catch (e: unknown) {\n const msg = `SshPl.platformaInit: error occurred: ${e}, state: ${JSON.stringify(state)}`;\n this.logger.error(msg);\n\n throw new Error(msg);\n }\n }\n\n public async downloadBinariesAndUploadToTheServer(\n localWorkdir: string,\n plBinary: PlBinarySourceDownload,\n remoteHome: string,\n arch: Arch,\n ) {\n const state: DownloadAndUntarState[] = [];\n try {\n const pl = await this.downloadAndUntar(\n localWorkdir, remoteHome, arch,\n 'pl', `pl-${plBinary.version}`,\n );\n state.push(pl);\n\n const supervisor = await this.downloadAndUntar(\n localWorkdir, remoteHome, arch,\n 'supervisord', plpath.supervisordDirName,\n );\n state.push(supervisor);\n\n const minioPath = plpath.minioBin(remoteHome, arch.arch);\n const minio = await this.downloadAndUntar(\n localWorkdir, remoteHome, arch,\n 'minio', plpath.minioDirName,\n );\n state.push(minio);\n await this.sshClient.chmod(minioPath, 0o750);\n\n return {\n history: state,\n minioRelPath: minioPath,\n downloadedPl: plpath.platformaBin(remoteHome, arch.arch),\n };\n } catch (e: unknown) {\n const msg = `SshPl.downloadBinariesAndUploadToServer: error ${e} occurred, state: ${JSON.stringify(state)}`;\n this.logger.error(msg);\n throw e;\n }\n }\n\n /** We have to extract pl in the remote server,\n * because Windows doesn't support symlinks\n * that are found in Linux pl binaries tgz archive.\n * For this reason, we extract all to the remote server.\n * It requires `tar` to be installed on the server\n * (it's not installed for Rocky Linux for example). */\n public async downloadAndUntar(\n localWorkdir: string,\n remoteHome: string,\n arch: Arch,\n softwareName: string,\n tgzName: string,\n ): Promise<DownloadAndUntarState> {\n const state: DownloadAndUntarState = {};\n state.binBasePath = plpath.binariesDir(remoteHome);\n await this.sshClient.ensureRemoteDirCreated(state.binBasePath);\n state.binBasePathCreated = true;\n\n let downloadBinaryResult: DownloadBinaryResult | null = null;\n const attempts = 5;\n for (let i = 1; i <= attempts; i++) {\n try {\n downloadBinaryResult = await downloadBinaryNoExtract(\n this.logger,\n localWorkdir,\n softwareName,\n tgzName,\n arch.arch, arch.platform,\n );\n break;\n } catch (e: unknown) {\n await sleep(300);\n if (i == attempts) {\n throw new Error(`downloadAndUntar: ${attempts} attempts, last error: ${e}`);\n }\n }\n }\n state.downloadResult = notEmpty(downloadBinaryResult);\n\n state.localArchivePath = upath.resolve(state.downloadResult.archivePath);\n state.remoteDir = upath.join(state.binBasePath, state.downloadResult.baseName);\n state.remoteArchivePath = state.remoteDir + '.tgz';\n\n await this.sshClient.ensureRemoteDirCreated(state.remoteDir);\n await this.sshClient.uploadFile(state.localArchivePath, state.remoteArchivePath);\n state.uploadDone = true;\n\n // TODO: Create a proper archive to avoid xattr warnings\n const untarResult = await this.sshClient.exec(\n `tar --warning=no-all -xvf ${state.remoteArchivePath} --directory=${state.remoteDir}`,\n );\n\n if (untarResult.stderr)\n throw Error(`downloadAndUntar: untar: stderr occurred: ${untarResult.stderr}, stdout: ${untarResult.stdout}`);\n\n state.untarDone = true;\n\n return state;\n }\n\n public async needDownload(remoteHome: string, arch: Arch) {\n const checkPathSupervisor = plpath.supervisorBin(remoteHome, arch.arch);\n const checkPathMinio = plpath.minioDir(remoteHome, arch.arch);\n const checkPathPlatforma = plpath.platformaBin(remoteHome, arch.arch);\n\n if (!await this.sshClient.checkFileExists(checkPathPlatforma)\n || !await this.sshClient.checkFileExists(checkPathMinio)\n || !await this.sshClient.checkFileExists(checkPathSupervisor)) {\n return true;\n }\n\n return false;\n }\n\n public async checkIsAliveWithInterval(interval: number = 1000, count = 15, shouldStart = true): Promise<void> {\n const maxMs = count * interval;\n\n let total = 0;\n let alive = await this.isAlive();\n while (shouldStart ? !alive.allAlive : alive.allAlive) {\n await sleep(interval);\n total += interval;\n if (total > maxMs) {\n throw new Error(`isAliveWithInterval: The process did not ${shouldStart ? 'started' : 'stopped'} after ${maxMs} ms. Live status: ${JSON.stringify(alive)}`);\n }\n alive = await this.isAlive();\n }\n }\n\n public async getUserCredentials(remoteHome: string): Promise<ConnectionInfo> {\n const connectionInfo = await this.sshClient.readFile(plpath.connectionInfo(remoteHome));\n return parseConnectionInfo(connectionInfo);\n }\n\n public async fetchPorts(remoteHome: string, arch: Arch): Promise<SshPlPorts> {\n const ports: SshPlPorts = {\n grpc: {\n local: await getFreePort(),\n remote: await this.getFreePortForPlatformaOnServer(remoteHome, arch),\n },\n monitoring: {\n local: await getFreePort(),\n remote: await this.getFreePortForPlatformaOnServer(remoteHome, arch),\n },\n debug: {\n local: await getFreePort(),\n remote: await this.getFreePortForPlatformaOnServer(remoteHome, arch),\n },\n minioPort: {\n local: await getFreePort(),\n remote: await this.getFreePortForPlatformaOnServer(remoteHome, arch),\n },\n minioConsolePort: {\n local: await getFreePort(),\n remote: await this.getFreePortForPlatformaOnServer(remoteHome, arch),\n },\n };\n\n return ports;\n }\n\n public async getLocalFreePort(): Promise<number> {\n return new Promise((res) => {\n const srv = net.createServer();\n srv.listen(0, () => {\n const port = (srv.address() as net.AddressInfo).port;\n srv.close((_) => res(port));\n });\n });\n }\n\n public async getFreePortForPlatformaOnServer(remoteHome: string, arch: Arch): Promise<number> {\n const freePortBin = plpath.platformaFreePortBin(remoteHome, arch.arch);\n\n const { stdout, stderr } = await this.sshClient.exec(`${freePortBin}`);\n if (stderr) {\n throw new Error(`getFreePortForPlatformaOnServer: stderr is not empty: ${stderr}, stdout: ${stdout}`);\n }\n\n return +stdout;\n }\n\n public async getArch(): Promise<Arch> {\n const { stdout, stderr } = await this.sshClient.exec('uname -s && uname -m');\n if (stderr)\n throw new Error(`getArch: stderr is not empty: ${stderr}, stdout: ${stdout}`);\n\n const arr = stdout.split('\\n');\n\n return {\n platform: arr[0],\n arch: arr[1],\n };\n }\n\n public async getUserHomeDirectory() {\n const { stdout, stderr } = await this.sshClient.exec('echo $HOME');\n\n if (stderr) {\n const home = `/home/${this.username}`;\n console.warn(`getUserHomeDirectory: stderr is not empty: ${stderr}, stdout: ${stdout}, will get a default home: ${home}`);\n\n return home;\n }\n\n return stdout.trim();\n }\n}\n\ntype Arch = { platform: string; arch: string };\n\nexport type SshPlConfig = {\n localWorkdir: string;\n license: PlLicenseMode;\n useGlobalAccess?: boolean;\n plBinary?: PlBinarySourceDownload;\n};\n\nconst defaultSshPlConfig: Pick<\n SshPlConfig,\n | 'useGlobalAccess'\n | 'plBinary'\n> = {\n useGlobalAccess: false,\n plBinary: {\n type: 'Download',\n version: getDefaultPlVersion(),\n },\n};\n\ntype BinPaths = {\n history?: DownloadAndUntarState[];\n minioRelPath: string;\n downloadedPl: string;\n};\n\ntype DownloadAndUntarState = {\n binBasePath?: string;\n binBasePathCreated?: boolean;\n downloadResult?: DownloadBinaryResult;\n attempts?: number;\n\n localArchivePath?: string;\n remoteDir?: string;\n remoteArchivePath?: string;\n uploadDone?: boolean;\n untarDone?: boolean;\n};\n\ntype PlatformaInitState = {\n localWorkdir?: string;\n plBinaryOps?: PlBinarySourceDownload;\n arch?: Arch;\n remoteHome?: string;\n alive?: SupervisorStatus;\n userCredentials?: ConnectionInfo;\n needRestart?: boolean;\n downloadedBinaries?: DownloadAndUntarState[];\n binPaths?: BinPaths;\n ports?: SshPlPorts;\n generatedConfig?: SshPlConfigGenerationResult;\n connectionInfo?: ConnectionInfo;\n started?: boolean;\n};\n"],"names":["processRun","logger","opts","spawn","isProcessAlive","pid","processStop","processWaitStopped","maxMs","total","sleep","OSes","newOs","osName","Arches","newArch","arch","cdn","gaCdn","downloadBinaryNoExtract","baseDir","softwareName","tgzName","platform","getPathsForDownload","archiveUrl","alternativeArchiveGAUrl","archivePath","downloadArchive","downloadBinary","archiveName","archiveType","targetFolder","baseName","extractArchive","os","osToArchiveType","archiveFileName","upath","dstArchiveFile","state","fileExists","fsp","body","statusCode","request","textBody","text","Readable","Writable","fs","e","msg","MarkerFileName","dstFolder","markerFilePath","tar","decompress","assertNever","getDefaultPlVersion","newDefaultPlBinarySource","resolveLocalPlBinaryPath","downloadDir","src","ops","osToBinaryName","filePid","dir","readPid","filePath","writePid","newTrace","trace","t","k","v","withTrace","fn","LocalConfigYaml","LocalPl","workingDir","startOptions","initialStartHistory","onClose","onError","onCloseAndError","onCloseAndErrorNoStop","__publicField","instance","pidFile","notEmpty","localPlatformaInit","_ops","workDir","localPlatformaReadPidAndStop","configPath","plBinPath","baseBinaryPath","processOpts","pl","file","oldPid","alive","defaultConfig","SshClient","client","config","withDefaults","Client","_a","_b","connect","command","resolve","reject","err","stream","stdout","stderr","code","data","host","port","conn","types","log","match","method","ports","randomBytes","persistentClient","RetryablePromise","p","server","net","localSocket","sockLog","forwardOut","s","rawAddress","address","hostname","dns","privateKey","ssh","localPath","remotePath","sftp","newErr","callback","mode","items","item","itemPath","error","path","list","a","b","directoryPath","message","buffer","stats","readFile","result","localDir","remoteDir","files","directories","currentPath","createNext","index","directory","localHost","localPort","remoteHost","remotePort","minioDirName","supervisordDirName","supervisordSubDirName","remoteHome","binariesDir","platformaBaseDir","platformaDir","platformaBin","platformaFreePortBin","minioDir","minioBin","supervisorBinDir","supervisorBin","supervisorConf","connectionInfo","supervisorCtlStart","sshClient","supervisorExec","supervisorStop","supervisorStatus","platforma","isProgramRunning","minio","status","generateSupervisordConfig","minioStorageDir","minioEnvs","supervisorRemotePort","remoteWorkDir","platformaConfigPath","minioPath","plPath","minioEnvStr","key","value","password","freePort","supervisorCmd","plpath.supervisorBin","plpath.supervisorConf","cmd","output","programName","str","line","name","PortPair","z","SshPlPorts","ConnectionInfo","newConnectionInfo","plUser","plPassword","useGlobalAccess","plVersion","parseConnectionInfo","content","stringifyConnectionInfo","SshPl","username","supervisorCtlShutdown","plpath.workDir","options","defaultSshPlConfig","sameGA","samePlVersion","downloadRes","generateSshPlConfigs","supervisorConfig","plpath.connectionInfo","localWorkdir","plBinary","supervisor","plpath.supervisordDirName","plpath.minioBin","plpath.minioDirName","plpath.platformaBin","plpath.binariesDir","downloadBinaryResult","attempts","i","untarResult","checkPathSupervisor","checkPathMinio","plpath.minioDir","checkPathPlatforma","interval","count","shouldStart","getFreePort","res","srv","_","freePortBin","plpath.platformaFreePortBin","arr","home"],"mappings":"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"}
|
package/dist/index.mjs
CHANGED
|
@@ -157,7 +157,7 @@ const $t = {
|
|
|
157
157
|
windows: "zip"
|
|
158
158
|
};
|
|
159
159
|
function R() {
|
|
160
|
-
return "1.
|
|
160
|
+
return "1.23.0";
|
|
161
161
|
}
|
|
162
162
|
function vt() {
|
|
163
163
|
return { type: "Download", version: R() };
|
|
@@ -267,7 +267,7 @@ async function fr(s, r) {
|
|
|
267
267
|
s.info(`writing configuration '${n}'...`), await m.writeFile(n, t.config);
|
|
268
268
|
const c = d.join(o, "binaries"), a = await Pt(s, c, t.plBinary), h = {
|
|
269
269
|
cmd: e("binaryPath", d.join("binaries", a)),
|
|
270
|
-
args: ["
|
|
270
|
+
args: ["--config", n],
|
|
271
271
|
opts: {
|
|
272
272
|
env: { ...process.env },
|
|
273
273
|
cwd: o,
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","sources":["../src/local/process.ts","../src/common/os_and_arch.ts","../src/common/pl_binary_download.ts","../src/common/pl_version.ts","../src/common/pl_binary.ts","../src/local/pid.ts","../src/local/trace.ts","../src/local/pl.ts","../src/ssh/ssh.ts","../src/ssh/pl_paths.ts","../src/ssh/supervisord.ts","../src/ssh/connection_info.ts","../src/ssh/pl.ts"],"sourcesContent":["import type { SpawnOptions, ChildProcess } from 'child_process';\nimport { spawn } from 'child_process';\nimport type { MiLogger } from '@milaboratories/ts-helpers';\nimport { sleep } from '@milaboratories/ts-helpers';\n\nexport type ProcessOptions = {\n cmd: string;\n args: string[];\n opts: SpawnOptions;\n};\n\nexport function processRun(logger: MiLogger, opts: ProcessOptions): ChildProcess {\n logger.info(`Running:\ncmd: ${JSON.stringify([opts.cmd, ...opts.args])}\nwd: ${opts.opts.cwd}`);\n\n logger.info(' spawning child process');\n return spawn(opts.cmd, opts.args, opts.opts);\n}\n\nexport async function isProcessAlive(pid: number) {\n try {\n process.kill(pid, 0);\n return true;\n } catch (e) {\n return false;\n }\n}\n\nexport function processStop(pid: number) {\n return process.kill(pid, 'SIGINT');\n}\n\nexport async function processWaitStopped(pid: number, maxMs: number) {\n const sleepMs = 100;\n let total = 0;\n while (await isProcessAlive(pid)) {\n await sleep(sleepMs);\n total += sleepMs;\n if (total > maxMs) {\n throw new Error(`The process did not stopped after ${maxMs} ms.`);\n }\n }\n}\n","import os from 'os';\n\nexport const OSes = ['linux', 'macos', 'windows'] as const;\nexport type OSType = (typeof OSes)[number];\n\n/** @param osName - should be the thing returned from either {@link os.platform())} or `uname -s` */\nexport function newOs(osName: string): OSType {\n switch (osName.toLowerCase()) {\n case 'darwin':\n return 'macos';\n case 'linux':\n return 'linux';\n case 'win32':\n return 'windows';\n default:\n throw new Error(\n `operating system '${osName}' is not currently supported by Platforma ecosystem. The list of OSes supported: `\n + JSON.stringify(OSes),\n );\n }\n}\n\nexport const Arches = ['amd64', 'arm64'] as const;\nexport type ArchType = (typeof Arches)[number];\n\n/** @param arch - should be the thing returned from either {@link os.arch())} or `uname -m` */\nexport function newArch(arch: string): ArchType {\n switch (arch) {\n case 'aarch64':\n case 'aarch64_be':\n case 'arm64':\n return 'arm64';\n\n case 'x86_64':\n case 'x64':\n return 'amd64';\n\n default:\n throw new Error(\n `processor architecture '${arch}' is not currently supported by Platforma ecosystem. The list of architectures supported: `\n + JSON.stringify(Arches),\n );\n }\n}\n","import fs from 'fs';\nimport fsp from 'fs/promises';\nimport upath from 'upath';\nimport { request } from 'undici';\nimport { Writable, Readable } from 'stream';\nimport { text } from 'stream/consumers';\nimport * as tar from 'tar';\nimport type { MiLogger } from '@milaboratories/ts-helpers';\nimport { assertNever, fileExists } from '@milaboratories/ts-helpers';\nimport decompress from 'decompress';\nimport type { ArchType, OSType } from './os_and_arch';\nimport { newOs, newArch } from './os_and_arch';\n\nconst cdn = 'https://cdn.platforma.bio/software';\n// We'll download things from Global Access if downloading from CDN has failed\n// (it might be that it's blocked from the company's network.)\nconst gaCdn = 'https://cdn-ga.pl-open.science/software';\n\nexport type DownloadBinaryResult = {\n archiveUrl: string;\n alternativeArchiveGAUrl: string;\n wasDownloadedFrom?: string;\n archivePath: string;\n archiveType: ArchiveType;\n targetFolder: string;\n baseName: string;\n};\n\nexport async function downloadBinaryNoExtract(\n logger: MiLogger,\n baseDir: string,\n softwareName: string,\n tgzName: string,\n arch: string,\n platform: string,\n): Promise<DownloadBinaryResult> {\n const opts = getPathsForDownload(softwareName, tgzName, baseDir, newArch(arch), newOs(platform));\n const { archiveUrl, alternativeArchiveGAUrl, archivePath } = opts;\n\n try {\n await downloadArchive(logger, archiveUrl, archivePath);\n opts.wasDownloadedFrom = archiveUrl;\n } catch (e: unknown) {\n await downloadArchive(logger, alternativeArchiveGAUrl, archivePath);\n opts.wasDownloadedFrom = alternativeArchiveGAUrl;\n }\n\n return opts;\n}\n\nexport async function downloadBinary(\n logger: MiLogger,\n baseDir: string,\n softwareName: string,\n archiveName: string,\n arch: string,\n platform: string,\n): Promise<DownloadBinaryResult> {\n const opts = getPathsForDownload(softwareName, archiveName, baseDir, newArch(arch), newOs(platform));\n const { archiveUrl, alternativeArchiveGAUrl, archivePath, archiveType, targetFolder, baseName } = opts;\n\n try {\n await downloadArchive(logger, archiveUrl, archivePath);\n opts.wasDownloadedFrom = archiveUrl;\n } catch (e: unknown) {\n await downloadArchive(logger, alternativeArchiveGAUrl, archivePath);\n opts.wasDownloadedFrom = alternativeArchiveGAUrl;\n }\n\n await extractArchive(logger, archivePath, archiveType, targetFolder);\n\n return opts;\n}\n\nfunction getPathsForDownload(\n softwareName: string,\n archiveName: string,\n baseDir: string,\n arch: ArchType,\n os: OSType,\n): DownloadBinaryResult {\n const baseName = `${archiveName}-${arch}`;\n const archiveType = osToArchiveType[os];\n\n const archiveFileName = `${baseName}.${archiveType}`;\n const archiveUrl = `${cdn}/${softwareName}/${os}/${archiveFileName}`;\n const alternativeArchiveGAUrl = `${gaCdn}/${softwareName}/${os}/${archiveFileName}`;\n const archivePath = upath.join(baseDir, archiveFileName);\n // folder where binary distribution of pl will be unpacked\n const targetFolder = upath.join(baseDir, baseName);\n\n return {\n archiveUrl,\n alternativeArchiveGAUrl,\n archivePath,\n archiveType,\n targetFolder,\n baseName,\n };\n}\n\nexport type DownloadInfo = {\n dstArchive?: string;\n fileExisted?: boolean;\n dirnameCreated?: boolean;\n statusCode?: number;\n errorMsg?: string;\n tmpPath?: string;\n wroteTmp?: boolean;\n tmpExisted?: boolean;\n renamed?: boolean;\n newExisted?: boolean;\n};\n\nexport async function downloadArchive(\n logger: MiLogger, archiveUrl: string, dstArchiveFile: string,\n): Promise<DownloadInfo> {\n const state: DownloadInfo = {};\n state.dstArchive = dstArchiveFile;\n\n try {\n state.fileExisted = await fileExists(dstArchiveFile);\n if (state.fileExisted) {\n logger.info(`Platforma Backend archive download skipped: '${dstArchiveFile}' already exists`);\n return state;\n }\n\n await fsp.mkdir(upath.dirname(dstArchiveFile), { recursive: true });\n state.dirnameCreated = true;\n\n logger.info(`Downloading archive:\\n URL: ${archiveUrl}\\n Save to: ${dstArchiveFile}`);\n\n const { body, statusCode } = await request(archiveUrl);\n state.statusCode = statusCode;\n if (statusCode != 200) {\n // completely draining the stream to prevent leaving open connections\n const textBody = await text(body);\n state.errorMsg = `failed to download archive: ${statusCode}, response: ${textBody.slice(0, 1000)}`;\n logger.error(state.errorMsg);\n throw new Error(state.errorMsg);\n }\n\n // to prevent incomplete downloads we first write in a temp file\n state.tmpPath = dstArchiveFile + '.tmp';\n await Readable.toWeb(body).pipeTo(Writable.toWeb(fs.createWriteStream(state.tmpPath)));\n state.wroteTmp = true;\n state.tmpExisted = await fileExists(state.tmpPath);\n\n // and then atomically rename it\n await fsp.rename(state.tmpPath, dstArchiveFile);\n state.renamed = true;\n state.newExisted = await fileExists(dstArchiveFile);\n\n return state;\n } catch (e: unknown) {\n const msg = `downloadArchive: error ${JSON.stringify(e)} occurred, state: ${JSON.stringify(state)}`;\n logger.error(msg);\n throw new Error(msg);\n }\n}\n\n/** Used to prevent mid-way interrupted unarchived folders to be used */\nconst MarkerFileName = '.ok';\n\nexport async function extractArchive(\n logger: MiLogger,\n archivePath: string,\n archiveType: ArchiveType,\n dstFolder: string,\n) {\n logger.info('extracting archive...');\n logger.info(` archive path: '${archivePath}'`);\n logger.info(` target dir: '${dstFolder}'`);\n\n if (!(await fileExists(archivePath))) {\n const msg = `Platforma Backend binary archive not found at '${archivePath}'`;\n logger.error(msg);\n throw new Error(msg);\n }\n\n const markerFilePath = upath.join(dstFolder, MarkerFileName);\n\n if (await fileExists(markerFilePath)) {\n logger.info(`Platforma Backend binaries unpack skipped: '${dstFolder}' exists`);\n return;\n }\n\n if (await fileExists(dstFolder)) {\n logger.info(`Removing previous incompletely unpacked folder: '${dstFolder}'`);\n await fsp.rm(dstFolder, { recursive: true });\n }\n\n logger.info(` creating target dir '${dstFolder}'`);\n await fsp.mkdir(dstFolder, { recursive: true });\n\n logger.info(\n `Unpacking Platforma Backend archive:\\n Archive: ${archivePath}\\n Target dir: ${dstFolder}`,\n );\n\n switch (archiveType) {\n case 'tgz':\n await tar.x({\n file: archivePath,\n cwd: dstFolder,\n gzip: true,\n });\n break;\n\n case 'zip':\n await decompress(archivePath, dstFolder);\n break;\n\n default:\n assertNever(archiveType);\n }\n\n // writing marker file, to be able in the future detect that we completely unarchived the tar file\n await fsp.writeFile(markerFilePath, 'ok');\n\n logger.info(` ... unpack done.`);\n}\n\nexport type ArchiveType = 'tgz' | 'zip';\n\nconst osToArchiveType: Record<OSType, ArchiveType> = {\n linux: 'tgz',\n macos: 'tgz',\n windows: 'zip',\n};\n","declare const PL_VERSION: string;\n\nexport function getDefaultPlVersion(): string {\n return PL_VERSION;\n}\n","import type { MiLogger } from '@milaboratories/ts-helpers';\nimport { assertNever } from '@milaboratories/ts-helpers';\nimport { downloadBinary } from './pl_binary_download';\nimport { getDefaultPlVersion } from './pl_version';\nimport os from 'os';\nimport upath from 'upath';\nimport type { OSType } from './os_and_arch';\nimport { newOs } from './os_and_arch';\n\n/** Shows how the binary should be got. */\nexport type PlBinarySource = PlBinarySourceDownload | PlBinarySourceLocal;\n\nexport type PlBinarySourceDownload = {\n readonly type: 'Download';\n readonly version: string;\n};\n\nexport type PlBinarySourceLocal = {\n readonly type: 'Local';\n readonly path: string;\n};\n\nexport function newDefaultPlBinarySource(): PlBinarySourceDownload {\n return { type: 'Download', version: getDefaultPlVersion() };\n}\n\nexport async function resolveLocalPlBinaryPath(\n logger: MiLogger,\n downloadDir: string,\n src: PlBinarySource,\n): Promise<string> {\n switch (src.type) {\n case 'Download':\n const ops = await downloadBinary(logger, downloadDir, 'pl', `pl-${src.version}`, os.arch(), os.platform());\n return upath.join(ops.baseName, 'binaries', osToBinaryName[newOs(os.platform())]);\n\n case 'Local':\n return src.path;\n\n default:\n assertNever(src);\n }\n}\n\nexport const osToBinaryName: Record<OSType, string> = {\n linux: 'platforma',\n macos: 'platforma',\n windows: 'platforma.exe',\n};\n","import { fileExists } from '@milaboratories/ts-helpers';\nimport fs from 'fs/promises';\nimport upath from 'upath';\n\nexport function filePid(dir: string) {\n return upath.join(dir, 'pl_pid');\n}\n\nexport async function readPid(filePath: string): Promise<number | undefined> {\n if (!(await fileExists(filePath))) {\n return undefined;\n }\n\n const text = await fs.readFile(filePath);\n\n return Number(text.toString());\n}\n\nexport async function writePid(filePath: string, pid: number) {\n await fs.writeFile(filePath, JSON.stringify(pid));\n}\n","import type { MiLogger } from '@milaboratories/ts-helpers';\n\n/** Records all inputs and outputs of one's choice, so if the error happened\n * one can check how it was by just printing this structure. */\nexport type Trace = Record<string, any>;\n\nexport function newTrace(): Trace {\n return {};\n}\n\nexport function trace(t: Trace, k: string, v: any) {\n t[k] = v;\n return v;\n}\n\n/** Creates a trace and runs a function with it. The function can record all its\n * logs or traces using `trace` fn. */\nexport async function withTrace<T>(\n logger: MiLogger,\n fn: (trace: (k: string, v: any) => typeof v, t: Trace) => Promise<T>,\n): Promise<T> {\n const t = newTrace();\n try {\n const result = await fn((k: string, v: any) => trace(t, k, v), t);\n return result;\n } catch (e: any) {\n logger.error(`error ${e} while doing traced operation, state: ${JSON.stringify(t)}`);\n throw e;\n }\n}\n","import type {\n ProcessOptions } from './process';\nimport {\n isProcessAlive,\n processStop,\n processWaitStopped,\n processRun,\n} from './process';\nimport type { PlBinarySource } from '../common/pl_binary';\nimport { newDefaultPlBinarySource, resolveLocalPlBinaryPath } from '../common/pl_binary';\nimport type { MiLogger } from '@milaboratories/ts-helpers';\nimport { notEmpty } from '@milaboratories/ts-helpers';\nimport type { ChildProcess, SpawnOptions } from 'child_process';\nimport { filePid, readPid, writePid } from './pid';\nimport type { Trace } from './trace';\nimport { withTrace } from './trace';\nimport upath from 'upath';\nimport fsp from 'fs/promises';\nimport type { Required } from 'utility-types';\n\nexport const LocalConfigYaml = 'config-local.yaml';\n\n/**\n * Represents a local running pl-core,\n * and has methods to start, check if it's running, stop and wait for stopping it.\n * Also, a hook on pl-core closed can be provided.\n */\nexport class LocalPl {\n private instance?: ChildProcess;\n public pid?: number;\n private nRuns: number = 0;\n private lastRunHistory: Trace = {};\n private wasStopped = false;\n\n constructor(\n private readonly logger: MiLogger,\n private readonly workingDir: string,\n private readonly startOptions: ProcessOptions,\n private readonly initialStartHistory: Trace,\n private readonly onClose?: (pl: LocalPl) => Promise<void>,\n private readonly onError?: (pl: LocalPl) => Promise<void>,\n private readonly onCloseAndError?: (pl: LocalPl) => Promise<void>,\n private readonly onCloseAndErrorNoStop?: (pl: LocalPl) => Promise<void>,\n ) {}\n\n async start() {\n await withTrace(this.logger, async (trace, t) => {\n this.wasStopped = false;\n const instance = processRun(this.logger, this.startOptions);\n instance.on('error', (e: any) => {\n this.logger.error(\n `error '${e}', while running platforma, started opts: ${JSON.stringify(this.debugInfo())}`,\n );\n\n // keep in mind there are no awaits here, it will be asynchronous\n if (this.onError !== undefined) this.onError(this);\n if (this.onCloseAndError !== undefined) this.onCloseAndError(this);\n if (this.onCloseAndErrorNoStop !== undefined && !this.wasStopped)\n this.onCloseAndErrorNoStop(this);\n });\n instance.on('close', () => {\n this.logger.warn(`platforma was closed, started opts: ${JSON.stringify(this.debugInfo())}`);\n\n // keep in mind there are no awaits here, it will be asynchronous\n if (this.onClose !== undefined) this.onClose(this);\n if (this.onCloseAndError !== undefined) this.onCloseAndError(this);\n if (this.onCloseAndErrorNoStop !== undefined && !this.wasStopped)\n this.onCloseAndErrorNoStop(this);\n });\n\n trace('started', true);\n\n const pidFile = trace('pidFile', filePid(this.workingDir));\n trace('pid', notEmpty(instance.pid));\n trace('pidWritten', await writePid(pidFile, notEmpty(instance.pid)));\n\n this.nRuns++;\n this.instance = instance;\n this.pid = instance.pid;\n this.lastRunHistory = t;\n });\n }\n\n stop() {\n // TODO use this.instance to stop the process\n this.wasStopped = true;\n processStop(notEmpty(this.pid));\n }\n\n async waitStopped() {\n await processWaitStopped(notEmpty(this.pid), 15000);\n }\n\n stopped() {\n return this.wasStopped;\n }\n\n async isAlive(): Promise<boolean> {\n return await isProcessAlive(notEmpty(this.pid));\n }\n\n debugInfo() {\n return {\n lastRunHistory: this.lastRunHistory,\n nRuns: this.nRuns,\n pid: this.pid,\n workingDir: this.workingDir,\n initialStartHistory: this.initialStartHistory,\n wasStopped: this.wasStopped,\n };\n }\n}\n\n/** Options to start a local pl-core. */\nexport type LocalPlOptions = {\n /** From what directory start a process. */\n readonly workingDir: string;\n /** A string representation of yaml config. */\n readonly config: string;\n /** How to get a binary, download it or get an existing one (default: download latest version) */\n readonly plBinary?: PlBinarySource;\n /** Additional options for a process, environments, stdout, stderr etc. */\n readonly spawnOptions?: SpawnOptions;\n /**\n * If the previous pl-core was started from the same directory,\n * we can check if it's still running and then stop it before starting a new one.\n * (default: true)\n */\n readonly closeOld?: boolean;\n\n readonly onClose?: (pl: LocalPl) => Promise<void>;\n readonly onError?: (pl: LocalPl) => Promise<void>;\n readonly onCloseAndError?: (pl: LocalPl) => Promise<void>;\n readonly onCloseAndErrorNoStop?: (pl: LocalPl) => Promise<void>;\n};\n\ntype LocalPlOptionsFull = Required<LocalPlOptions, 'plBinary' | 'spawnOptions' | 'closeOld'>;\n\n/**\n * Starts pl-core, if the option was provided downloads a binary, reads license environments etc.\n */\nexport async function localPlatformaInit(logger: MiLogger, _ops: LocalPlOptions): Promise<LocalPl> {\n // filling-in default values\n const ops = {\n plBinary: newDefaultPlBinarySource(),\n spawnOptions: {},\n closeOld: true,\n ..._ops,\n } satisfies LocalPlOptionsFull;\n\n return await withTrace(logger, async (trace, t) => {\n trace('startOptions', { ...ops, config: 'too wordy' });\n\n const workDir = upath.resolve(ops.workingDir);\n\n if (ops.closeOld) {\n trace('closeOld', await localPlatformaReadPidAndStop(logger, workDir));\n }\n\n const configPath = upath.join(workDir, LocalConfigYaml);\n\n logger.info(`writing configuration '${configPath}'...`);\n await fsp.writeFile(configPath, ops.config);\n\n const plBinPath = upath.join(workDir, 'binaries');\n const baseBinaryPath = await resolveLocalPlBinaryPath(logger, plBinPath, ops.plBinary);\n\n const binaryPath = trace('binaryPath', upath.join('binaries', baseBinaryPath));\n\n const processOpts: ProcessOptions = {\n cmd: binaryPath,\n args: ['-config', configPath],\n opts: {\n env: { ...process.env },\n cwd: workDir,\n stdio: ['pipe', 'ignore', 'inherit'],\n windowsHide: true, // hide a terminal on Windows\n ...ops.spawnOptions,\n },\n };\n trace('processOpts', {\n cmd: processOpts.cmd,\n args: processOpts.args,\n cwd: processOpts.opts.cwd,\n });\n\n const pl = new LocalPl(\n logger,\n ops.workingDir,\n processOpts,\n t,\n ops.onClose,\n ops.onError,\n ops.onCloseAndError,\n ops.onCloseAndErrorNoStop,\n );\n await pl.start();\n\n return pl;\n });\n}\n\n/** Reads a pid of the old pl-core if it was started in the same working directory,\n * and closes it. */\nasync function localPlatformaReadPidAndStop(\n logger: MiLogger,\n workingDir: string,\n): Promise<Record<string, any>> {\n return await withTrace(logger, async (trace, t) => {\n const file = trace('pidFilePath', filePid(workingDir));\n\n const oldPid = trace('pid', await readPid(file));\n const alive = trace('wasAlive', await isProcessAlive(oldPid));\n\n if (oldPid !== undefined && alive) {\n trace('stopped', processStop(oldPid));\n trace('waitStopped', await processWaitStopped(oldPid, 10_000));\n }\n\n return t;\n });\n}\n","import type { ConnectConfig, ClientChannel, SFTPWrapper } from 'ssh2';\nimport ssh, { Client } from 'ssh2';\nimport net from 'net';\nimport dns from 'dns';\nimport fs from 'fs';\nimport { readFile } from 'fs/promises';\nimport upath from 'upath';\nimport { RetryablePromise, type MiLogger } from '@milaboratories/ts-helpers';\nimport { randomBytes } from 'crypto';\n\nconst defaultConfig: ConnectConfig = {\n keepaliveInterval: 60000,\n keepaliveCountMax: 10,\n};\n\nexport type SshAuthMethods = 'publickey' | 'password';\nexport type SshAuthMethodsResult = SshAuthMethods[];\nexport type SshDirContent = {\n files: string[];\n directories: string[];\n};\n\nexport class SshClient {\n private config?: ConnectConfig;\n public homeDir?: string;\n private forwardedServers: net.Server[] = [];\n\n constructor(\n private readonly logger: MiLogger,\n private readonly client: Client,\n ) {}\n\n /**\n * Initializes the SshClient and establishes a connection using the provided configuration.\n * @param config - The connection configuration object for the SSH client.\n * @returns A new instance of SshClient with an active connection.\n */\n public static async init(logger: MiLogger, config: ConnectConfig): Promise<SshClient> {\n const withDefaults = {\n ...defaultConfig,\n ...config,\n };\n\n const client = new SshClient(logger, new Client());\n await client.connect(withDefaults);\n\n return client;\n }\n\n public getForwardedServers() {\n return this.forwardedServers;\n }\n\n public getFullHostName() {\n return `${this.config?.host}:${this.config?.port}`;\n }\n\n public getUserName() {\n return this.config?.username;\n }\n\n /**\n * Connects to the SSH server using the specified configuration.\n * @param config - The connection configuration object for the SSH client.\n * @returns A promise that resolves when the connection is established or rejects on error.\n */\n public async connect(config: ConnectConfig) {\n this.config = config;\n return await connect(this.client, config, () => {}, () => {});\n }\n\n /**\n * Executes a command on the SSH server.\n * @param command - The command to execute on the remote server.\n * @returns A promise resolving with the command's stdout and stderr outputs.\n */\n public async exec(command: string): Promise<SshExecResult> {\n return new Promise((resolve, reject) => {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n this.client.exec(command, (err: any, stream: ClientChannel) => {\n if (err) {\n return reject(`ssh.exec: ${command}, error occurred: ${err}`);\n }\n\n let stdout = '';\n let stderr = '';\n\n stream.on('close', (code: number) => {\n if (code === 0) {\n resolve({ stdout, stderr });\n } else {\n reject(new Error(`Command ${command} exited with code ${code}`));\n }\n }).on('data', (data: ArrayBuffer) => {\n stdout += data.toString();\n }).stderr.on('data', (data: ArrayBuffer) => {\n stderr += data.toString();\n });\n });\n });\n }\n\n /**\n * Retrieves the supported authentication methods for a given host and port.\n * @param host - The hostname or IP address of the server.\n * @param port - The port number to connect to on the server.\n * @returns 'publickey' | 'password'[] A promise resolving with a list of supported authentication methods.\n */\n public static async getAuthTypes(host: string, port: number): Promise<SshAuthMethodsResult> {\n return new Promise((resolve) => {\n let stdout = '';\n const conn = new Client();\n\n conn.on('ready', () => {\n conn.end();\n const types = this.extractAuthMethods(stdout);\n resolve(types.length === 0 ? ['publickey', 'password'] : types as SshAuthMethodsResult);\n });\n\n conn.on('error', () => {\n conn.end();\n resolve(['publickey', 'password']);\n });\n\n conn.connect({\n host,\n port,\n username: new Date().getTime().toString(),\n debug: (err) => {\n stdout += `${err}\\n`;\n },\n });\n });\n }\n\n /**\n * Extracts authentication methods from debug logs.\n * @param log - The debug log output containing authentication information.\n * @returns An array of extracted authentication methods.\n */\n private static extractAuthMethods(log: string): string[] {\n const match = log.match(/Inbound: Received USERAUTH_FAILURE \\((.+)\\)/);\n return match && match[1] ? match[1].split(',').map((method) => method.trim()) : [];\n }\n\n /**\n * Sets up port forwarding between a remote port on the SSH server and a local port.\n * A new connection is used for this operation instead of an existing one.\n * @param ports - An object specifying the remote and local port configuration.\n * @param config - Optional connection configuration for the SSH client.\n * @returns { server: net.Server } A promise resolving with the created server instance.\n */\n public async forwardPort(ports: { remotePort: number; localPort: number; localHost?: string }, config?: ConnectConfig): Promise<{ server: net.Server }> {\n const log = `ssh.forward:${ports.localPort}:${ports.remotePort}.id_${randomBytes(1).toString('hex')}`;\n config = config ?? this.config;\n\n // we make this thing persistent so that if the connection\n // drops (it happened in the past because of lots of errors and forwardOut opened channels),\n // we'll recreate it here.\n const persistentClient = new RetryablePromise((p: RetryablePromise<Client>) => {\n return new Promise<Client>((resolve, reject) => {\n const client = new Client();\n\n client.on('ready', () => {\n this.logger.info(`${log}.client.ready`);\n resolve(client);\n });\n\n client.on('error', (err) => {\n this.logger.info(`${log}.client.error: ${err}`);\n p.reset();\n reject(err);\n });\n\n client.on('close', () => {\n this.logger.info(`${log}.client.closed`);\n p.reset();\n });\n\n client.connect(config!);\n });\n });\n\n await persistentClient.ensure(); // warm up a connection\n\n return new Promise((resolve, reject) => {\n const server = net.createServer({ pauseOnConnect: true }, async (localSocket) => {\n const sockLog = `${log}.sock_${randomBytes(1).toString('hex')}`;\n // this.logger.info(`${sockLog}.localSocket: start connection`);\n let conn: Client;\n try {\n conn = await persistentClient.ensure();\n } catch (e: unknown) {\n this.logger.info(`${sockLog}.persistentClient.catch: ${e}`);\n localSocket.end();\n return;\n }\n\n let stream: ClientChannel;\n try {\n stream = await forwardOut(this.logger, conn, '127.0.0.1', 0, '127.0.0.1', ports.remotePort);\n } catch (e: unknown) {\n this.logger.error(`${sockLog}.forwardOut.err: ${e}`);\n localSocket.end();\n return;\n }\n\n localSocket.pipe(stream);\n stream.pipe(localSocket);\n localSocket.resume();\n // this.logger.info(`${sockLog}.forwardOut: connected`);\n\n stream.on('error', (err: unknown) => {\n this.logger.error(`${sockLog}.stream.error: ${err}`);\n localSocket.end();\n stream.end();\n });\n stream.on('close', () => {\n // this.logger.info(`${sockLog}.stream.close: closed`);\n localSocket.end();\n stream.end();\n });\n localSocket.on('close', () => {\n this.logger.info(`${sockLog}.localSocket: closed`);\n localSocket.end();\n stream.end();\n });\n });\n\n server.listen(ports.localPort, '127.0.0.1', () => {\n this.logger.info(`${log}.server: started listening`);\n this.forwardedServers.push(server);\n resolve({ server });\n });\n\n server.on('error', (err) => {\n server.close();\n reject(new Error(`${log}.server: error: ${JSON.stringify(err)}`));\n });\n\n server.on('close', () => {\n this.logger.info(`${log}.server: closed ${JSON.stringify(ports)}`);\n this.forwardedServers = this.forwardedServers.filter((s) => s !== server);\n });\n });\n }\n\n public closeForwardedPorts(): void {\n this.logger.info('[SSH] Closing all forwarded ports...');\n this.forwardedServers.forEach((server) => {\n const rawAddress = server.address();\n if (rawAddress && typeof rawAddress !== 'string') {\n const address: net.AddressInfo = rawAddress;\n this.logger.info(`[SSH] Closing port forward for server ${address.address}:${address.port}`);\n }\n\n server.close();\n });\n this.forwardedServers = [];\n }\n\n /**\n * Checks if a specified host is available by performing a DNS lookup.\n * @param hostname - The hostname or IP address to check.\n * @returns A promise resolving with `true` if the host is reachable, otherwise `false`.\n */\n public static async checkHostAvailability(hostname: string): Promise<boolean> {\n return new Promise((resolve) => {\n dns.lookup(hostname, (err) => {\n resolve(!err);\n });\n });\n }\n\n /**\n * Determines whether a private key requires a passphrase for use.\n * @param privateKey - The private key content to check.\n * @returns A promise resolving with `true` if a passphrase is required, otherwise `false`.\n */\n public static async isPassphraseRequiredForKey(privateKey: string): Promise<boolean> {\n return new Promise((resolve, reject) => {\n try {\n const keyOrError = ssh.utils.parseKey(privateKey);\n if (keyOrError instanceof Error) {\n resolve(true);\n }\n return resolve(false);\n } catch (err: unknown) {\n console.log('Error parsing privateKey');\n reject(new Error(`ssh.isPassphraseRequiredForKey: err ${err}`));\n }\n });\n }\n\n /**\n * Uploads a local file to a remote server via SFTP.\n * This function creates new SFTP connection\n * @param localPath - The local file path.\n * @param remotePath - The remote file path on the server.\n * @returns A promise resolving with `true` if the file was successfully uploaded.\n */\n public async uploadFile(localPath: string, remotePath: string): Promise<boolean> {\n return await this.withSftp(async (sftp) => {\n return new Promise((resolve, reject) => {\n sftp.fastPut(localPath, remotePath, (err) => {\n if (err) {\n const newErr = new Error(\n `ssh.uploadFile: err: ${err}, localPath: ${localPath}, remotePath: ${remotePath}`);\n return reject(newErr);\n }\n resolve(true);\n });\n });\n });\n }\n\n public async withSftp<R>(callback: (sftp: SFTPWrapper) => Promise<R>): Promise<R> {\n return new Promise((resolve, reject) => {\n this.client.sftp((err, sftp) => {\n if (err) {\n return reject(new Error(`ssh.withSftp: sftp err: ${err}`));\n }\n\n callback(sftp)\n .then(resolve)\n .catch((err) => {\n reject(new Error(`ssh.withSftp.callback: err ${err}`));\n })\n .finally(() => {\n sftp?.end();\n });\n });\n });\n }\n\n public async writeFileOnTheServer(remotePath: string, data: string | Buffer, mode: number = 0o660) {\n return this.withSftp(async (sftp) => {\n return this.writeFile(sftp, remotePath, data, mode);\n });\n }\n\n public async getForderStructure(sftp: SFTPWrapper, remotePath: string, data: SshDirContent = { files: [], directories: [] }): Promise<SshDirContent> {\n return new Promise((resolve, reject) => {\n sftp.readdir(remotePath, async (err, items) => {\n if (err) {\n return reject(err);\n }\n\n for (const item of items) {\n const itemPath = `${remotePath}/${item.filename}`;\n if (item.attrs.isDirectory()) {\n data.directories.push(itemPath);\n try {\n await this.getForderStructure(sftp, itemPath, data);\n } catch (error) {\n return reject(error);\n }\n } else {\n data.files.push(itemPath);\n }\n }\n resolve(data);\n });\n });\n }\n\n public rmdir(sftp: SFTPWrapper, path: string) {\n return new Promise((resolve, reject) => {\n sftp.rmdir(path, (err) => err ? reject(err) : resolve(true));\n });\n }\n\n public unlink(sftp: SFTPWrapper, path: string) {\n return new Promise((resolve, reject) => {\n sftp.unlink(path, (err) => err ? reject(err) : resolve(true));\n });\n }\n\n public async deleteFolder(path: string) {\n return this.withSftp(async (sftp) => {\n try {\n const list = await this.getForderStructure(sftp, path);\n this.logger.info(`ssh.deleteFolder list of files and directories`);\n this.logger.info(`ssh.deleteFolder list of files: ${list.files}`);\n this.logger.info(`ssh.deleteFolder list of directories: ${list.directories}`);\n\n for (const filePath of list.files) {\n this.logger.info(`ssh.deleteFolder unlink file ${filePath}`);\n await this.unlink(sftp, filePath);\n }\n\n list.directories.sort((a, b) => b.length - a.length);\n\n for (const directoryPath of list.directories) {\n this.logger.info(`ssh.deleteFolder rmdir ${directoryPath}`);\n await this.rmdir(sftp, directoryPath);\n }\n\n await this.rmdir(sftp, path);\n return true;\n } catch (e: unknown) {\n this.logger.error(e);\n const message = e instanceof Error ? e.message : '';\n throw new Error(`ssh.deleteFolder: path: ${path}, message: ${message}`);\n }\n });\n }\n\n public async readFile(remotePath: string): Promise<string> {\n return this.withSftp(async (sftp) => {\n return new Promise((resolve, reject) => {\n sftp.readFile(remotePath, (err, buffer) => {\n if (err) {\n return reject(new Error(`ssh.readFile: err occurred ${err}`));\n }\n resolve(buffer.toString());\n });\n });\n });\n }\n\n async chmod(path: string, mode: number) {\n return this.withSftp(async (sftp) => {\n return new Promise((resolve, reject) => {\n sftp.chmod(path, mode, (err) => {\n if (err) {\n return reject(new Error(`ssh.chmod: ${err}, path: ${path}, mode: ${mode}`));\n }\n return resolve(undefined);\n });\n });\n });\n }\n\n async checkFileExists(remotePath: string) {\n return this.withSftp(async (sftp) => {\n return new Promise((resolve, reject) => {\n sftp.stat(remotePath, (err: Error | undefined, stats) => {\n if (err) {\n if ((err as unknown as { code?: number })?.code === 2) {\n return resolve(false);\n }\n return reject(new Error(`ssh.checkFileExists: err ${err}`));\n }\n resolve(stats.isFile());\n });\n });\n });\n }\n\n async checkPathExists(remotePath: string): Promise<{ exists: boolean; isFile: boolean; isDirectory: boolean }> {\n return this.withSftp(async (sftp) => {\n return new Promise((resolve, reject) => {\n sftp.stat(remotePath, (err, stats) => {\n if (err) {\n if ((err as Error & { code: number }).code === 2) {\n return resolve({ exists: false, isFile: false, isDirectory: false });\n }\n return reject(new Error(`ssh.checkPathExists: ${err}`));\n }\n resolve({\n exists: true,\n isFile: stats.isFile(),\n isDirectory: stats.isDirectory(),\n });\n });\n });\n });\n }\n\n private async writeFile(sftp: SFTPWrapper, remotePath: string, data: string | Buffer, mode: number = 0o660): Promise<boolean> {\n return new Promise((resolve, reject) => {\n sftp.writeFile(remotePath, data, { mode }, (err) => {\n if (err) {\n return reject(new Error(`ssh.writeFile: err ${err}, remotePath: ${remotePath}`));\n }\n resolve(true);\n });\n });\n }\n\n public uploadFileUsingExistingSftp(sftp: SFTPWrapper, localPath: string, remotePath: string, mode: number = 0o660) {\n return new Promise((resolve, reject) => {\n readFile(localPath).then(async (result: Buffer) => {\n this.writeFile(sftp, remotePath, result, mode)\n .then(() => {\n resolve(undefined);\n })\n .catch((err) => {\n const msg = `uploadFileUsingExistingSftp: error ${err} occurred`;\n this.logger.error(msg);\n reject(new Error(msg));\n });\n });\n });\n }\n\n private async __uploadDirectory(sftp: SFTPWrapper, localDir: string, remoteDir: string, mode: number = 0o660): Promise<void> {\n return new Promise((resolve, reject) => {\n fs.readdir(localDir, async (err, files) => {\n if (err) {\n return reject(new Error(`ssh.__uploadDir: err ${err}, localDir: ${localDir}, remoteDir: ${remoteDir}`));\n }\n\n try {\n await this.__createRemoteDirectory(sftp, remoteDir);\n for (const file of files) {\n const localPath = upath.join(localDir, file);\n const remotePath = `${remoteDir}/${file}`;\n\n if (fs.lstatSync(localPath).isDirectory()) {\n await this.__uploadDirectory(sftp, localPath, remotePath, mode);\n } else {\n await this.uploadFileUsingExistingSftp(sftp, localPath, remotePath, mode);\n }\n }\n\n resolve();\n } catch (err) {\n const msg = `ssh.__uploadDir: catched err ${err}`;\n this.logger.error(msg);\n reject(new Error(msg));\n }\n });\n });\n }\n\n /**\n * Uploads a local directory and its contents (including subdirectories) to the remote server via SFTP.\n * @param localDir - The path to the local directory to upload.\n * @param remoteDir - The path to the remote directory on the server.\n * @returns A promise that resolves when the directory and its contents are uploaded.\n */\n public async uploadDirectory(localDir: string, remoteDir: string, mode: number = 0o660): Promise<void> {\n return new Promise((resolve, reject) => {\n this.withSftp(async (sftp: SFTPWrapper) => {\n try {\n await this.__uploadDirectory(sftp, localDir, remoteDir, mode);\n resolve();\n } catch (e: unknown) {\n reject(new Error(`ssh.uploadDirectory: ${e}`));\n }\n });\n });\n }\n\n /**\n * Ensures that a remote directory and all its parent directories exist.\n * @param sftp - The SFTP wrapper.\n * @param remotePath - The path to the remote directory.\n * @returns A promise that resolves when the directory is created.\n */\n private __createRemoteDirectory(sftp: SFTPWrapper, remotePath: string): Promise<void> {\n return new Promise((resolve, reject) => {\n const directories = remotePath.split('/');\n let currentPath = '';\n\n const createNext = (index: number) => {\n if (index >= directories.length) {\n return resolve();\n }\n\n currentPath += `${directories[index]}/`;\n\n sftp.stat(currentPath, (err) => {\n if (err) {\n sftp.mkdir(currentPath, (err) => {\n if (err) {\n return reject(new Error(`ssh.__createRemDir: err ${err}, remotePath: ${remotePath}`));\n }\n createNext(index + 1);\n });\n } else {\n createNext(index + 1);\n }\n });\n };\n\n createNext(0);\n });\n }\n\n /**\n * Ensures that a remote directory and all its parent directories exist.\n * @param sftp - The SFTP wrapper.\n * @param remotePath - The path to the remote directory.\n * @returns A promise that resolves when the directory is created.\n */\n public ensureRemoteDirCreated(remotePath: string, mode: number = 0o755): Promise<void> {\n return this.withSftp(async (sftp) => {\n const directories = remotePath.split('/');\n let currentPath = '';\n\n for (const directory of directories) {\n currentPath += `${directory}/`;\n\n try {\n await new Promise<void>((resolve, reject) => {\n sftp.stat(currentPath, (err) => {\n if (!err) return resolve();\n\n sftp.mkdir(currentPath, { mode }, (err) => {\n if (err) {\n return reject(new Error(`ssh.createRemoteDir: err ${err}, remotePath: ${remotePath}`));\n }\n resolve();\n });\n });\n });\n } catch (error) {\n console.error(`Failed to create directory: ${currentPath}`, error);\n throw error;\n }\n }\n });\n }\n\n /**\n * Downloads a file from the remote server to a local path via SFTP.\n * @param remotePath - The remote file path on the server.\n * @param localPath - The local file path to save the file.\n * @returns A promise resolving with `true` if the file was successfully downloaded.\n */\n public async downloadFile(remotePath: string, localPath: string): Promise<boolean> {\n return this.withSftp(async (sftp) => {\n return new Promise((resolve, reject) => {\n sftp.fastGet(remotePath, localPath, (err) => {\n if (err) {\n return reject(new Error(`ssh.downloadFile: err ${err}, remotePath: ${remotePath}, localPath: ${localPath}`));\n }\n resolve(true);\n });\n });\n });\n }\n\n /**\n * Closes the SSH client connection and forwarded ports.\n */\n public close(): void {\n this.closeForwardedPorts();\n this.client.end();\n }\n}\n\nexport type SshExecResult = { stdout: string; stderr: string };\n\nasync function connect(\n client: Client,\n config: ConnectConfig,\n onError: (e: unknown) => void,\n onClose: () => void,\n): Promise<Client> {\n return new Promise((resolve, reject) => {\n client.on('ready', () => {\n resolve(client);\n });\n\n client.on('error', (err: unknown) => {\n onError(err);\n reject(new Error(`ssh.connect: error occurred: ${err}`));\n });\n\n client.on('close', () => {\n onClose();\n });\n\n client.connect(config);\n });\n}\n\nasync function forwardOut(logger: MiLogger, conn: Client, localHost: string, localPort: number, remoteHost: string, remotePort: number): Promise<ClientChannel> {\n return new Promise((resolve, reject) => {\n conn.forwardOut(localHost, localPort, remoteHost, remotePort, (err, stream) => {\n if (err) {\n logger.error(`forwardOut.error: ${err}`);\n return reject(err);\n }\n\n return resolve(stream);\n });\n });\n}\n","/** Just a lot of hardcoded paths of our current ssh deployment. */\n\nimport upath from 'upath';\nimport { newArch } from '../common/os_and_arch';\nimport { getDefaultPlVersion } from '../common/pl_version';\n\nexport const minioDirName = 'minio-2024-12-18T13-15-44Z';\nexport const supervisordDirName = 'supervisord-0.7.3';\nexport const supervisordSubDirName = 'supervisord_0.7.3_Linux_64-bit';\n\nexport function workDir(remoteHome: string) {\n return upath.join(remoteHome, '.platforma_ssh');\n}\n\nexport function binariesDir(remoteHome: string) {\n return upath.join(workDir(remoteHome), 'binaries');\n}\n\nexport function platformaBaseDir(remoteHome: string, arch: string) {\n return upath.join(binariesDir(remoteHome), `pl-${getDefaultPlVersion()}-${newArch(arch)}`);\n}\n\nexport function platformaDir(remoteHome: string, arch: string) {\n return upath.join(platformaBaseDir(remoteHome, arch), 'binaries');\n}\n\nexport function platformaBin(remoteHome: string, arch: string): string {\n return upath.join(platformaDir(remoteHome, arch), 'platforma');\n}\n\nexport function platformaConf(remoteHome: string): string {\n return upath.join(workDir(remoteHome), 'config.yaml');\n}\n\nexport function platformaFreePortBin(remoteHome: string, arch: string): string {\n return upath.join(platformaDir(remoteHome, arch), 'free-port');\n}\n\nexport function minioDir(remoteHome: string, arch: string) {\n return upath.join(binariesDir(remoteHome), `minio-2024-12-18T13-15-44Z-${newArch(arch)}`);\n}\n\nexport function minioBin(remoteHome: string, arch: string) {\n return upath.join(minioDir(remoteHome, arch), 'minio');\n}\n\nexport function supervisorBinDir(remoteHome: string, arch: string) {\n return upath.join(binariesDir(remoteHome), `supervisord-0.7.3-${newArch(arch)}`, supervisordSubDirName);\n}\n\nexport function supervisorBin(remoteHome: string, arch: string): string {\n return upath.join(supervisorBinDir(remoteHome, arch), 'supervisord');\n}\n\nexport function supervisorConf(remoteHome: string) {\n return upath.join(workDir(remoteHome), 'supervisor.conf');\n}\n\nexport function connectionInfo(remoteHome: string) {\n return upath.join(workDir(remoteHome), `connection.txt`);\n}\n","/** Provides helper functions to work with supervisord */\n\nimport type { MiLogger } from '@milaboratories/ts-helpers';\nimport * as plpath from './pl_paths';\nimport type { SshClient, SshExecResult } from './ssh';\nimport { randomBytes } from 'crypto';\n\nexport async function supervisorCtlStart(\n sshClient: SshClient,\n remoteHome: string, arch: string,\n) {\n const result = await supervisorExec(sshClient, remoteHome, arch, '--daemon');\n\n if (result.stderr) {\n throw new Error(`Can not run ssh Platforma ${result.stderr}`);\n }\n}\n\nexport async function supervisorStop(\n sshClient: SshClient,\n remoteHome: string, arch: string,\n) {\n const result = await supervisorExec(sshClient, remoteHome, arch, 'ctl shutdown');\n\n if (result.stderr) {\n throw new Error(`Can not stop ssh Platforma ${result.stderr}`);\n }\n}\n\n/** Provides a simple true/false response got from supervisord status\n * along with a debug info that could be showed in error logs (raw response from the command, parsed response etc). */\nexport type SupervisorStatus = {\n platforma?: boolean;\n minio?: boolean;\n allAlive: boolean; // true when both pl and minio are alive.\n rawResult?: SshExecResult;\n execError?: string;\n};\n\nexport async function supervisorStatus(\n logger: MiLogger,\n sshClient: SshClient,\n remoteHome: string, arch: string,\n): Promise<SupervisorStatus> {\n let result: SshExecResult;\n try {\n result = await supervisorExec(sshClient, remoteHome, arch, 'ctl status');\n } catch (e: unknown) {\n return { execError: String(e), allAlive: false };\n }\n\n if (result.stderr) {\n logger.info(`supervisord ctl status: stderr occurred: ${result.stderr}, stdout: ${result.stdout}`);\n\n return { rawResult: result, allAlive: false };\n }\n\n const platforma = isProgramRunning(result.stdout, 'platforma');\n const minio = isProgramRunning(result.stdout, 'minio');\n const status: SupervisorStatus = {\n rawResult: result,\n platforma,\n minio,\n allAlive: platforma && minio,\n };\n\n if (status.allAlive) {\n return status;\n }\n\n if (!status.minio) {\n logger.warn('Minio is not running on the server');\n }\n\n if (!status.platforma) {\n logger.warn('Platforma is not running on the server');\n }\n\n return status;\n}\n\nexport function generateSupervisordConfig(\n minioStorageDir: string,\n minioEnvs: Record<string, string>,\n supervisorRemotePort: number,\n remoteWorkDir: string,\n platformaConfigPath: string,\n\n minioPath: string,\n plPath: string,\n) {\n const minioEnvStr = Object.entries(minioEnvs).map(([key, value]) => `${key}=\"${value}\"`).join(',');\n const password = randomBytes(16).toString('hex');\n const freePort = supervisorRemotePort;\n\n return `\n[supervisord]\nlogfile=${remoteWorkDir}/supervisord.log\nloglevel=info\npidfile=${remoteWorkDir}/supervisord.pid\n\n[inet_http_server]\nport=127.0.0.1:${freePort}\nusername=default-user\npassword=${password}\n\n[supervisorctl]\nserverurl=http://127.0.0.1:${freePort}\nusername=default-user\npassword=${password}\n\n[program:platforma]\nautostart=true\ndepends_on=minio\ncommand=${plPath} --config ${platformaConfigPath}\ndirectory=${remoteWorkDir}\nautorestart=true\n\n[program:minio]\nautostart=true\nenvironment=${minioEnvStr}\ncommand=${minioPath} server ${minioStorageDir}\ndirectory=${remoteWorkDir}\nautorestart=true\n`;\n}\n\nexport async function supervisorExec(\n sshClient: SshClient,\n remoteHome: string, arch: string,\n command: string,\n) {\n const supervisorCmd = plpath.supervisorBin(remoteHome, arch);\n const supervisorConf = plpath.supervisorConf(remoteHome);\n\n const cmd = `${supervisorCmd} --configuration ${supervisorConf} ${command}`;\n return await sshClient.exec(cmd);\n}\n\nfunction isProgramRunning(output: string, programName: string) {\n // eslint-disable-next-line no-control-regex\n const stripAnsi = (str: string) => str.replace(/\\x1B\\[[0-9;]*m/g, '');\n\n const cleanedOutput = stripAnsi(output);\n\n return cleanedOutput.split('\\n').some((line) => {\n const [name, status] = line.trim().split(/\\s{2,}/); // Split string by 2 spaces.\n\n return name === programName && status === 'Running';\n });\n}\n","/** We store all info about the connection on the server,\n * so that another client could read the file and connect from another machine. */\nimport { z } from 'zod';\n\n//\n// Types\n//\n\nexport const PortPair = z.object({\n local: z.number(),\n remote: z.number(),\n});\n/** The pair of ports for forwarding. */\nexport type PortPair = z.infer<typeof PortPair>;\n\nexport const SshPlPorts = z.object({\n grpc: PortPair,\n monitoring: PortPair,\n debug: PortPair,\n minioPort: PortPair,\n minioConsolePort: PortPair,\n});\n/** All info about ports that are forwarded. */\nexport type SshPlPorts = z.infer<typeof SshPlPorts>;\n\nexport const ConnectionInfo = z.object({\n plUser: z.string(),\n plPassword: z.string(),\n ports: SshPlPorts,\n\n // It's false by default because it was added later,\n // and in some deployments there won't be useGlobalAccess flag in the file.\n useGlobalAccess: z.boolean().default(false),\n\n // We added the field afterwards, the pl backend was this version.\n plVersion: z.string().default('1.18.3'),\n});\n/** The content of the file that holds all the info about the connection on the remote server. */\nexport type ConnectionInfo = z.infer<typeof ConnectionInfo>;\n\n//\n// Funcs\n//\n\nexport function newConnectionInfo(\n plUser: string,\n plPassword: string,\n ports: SshPlPorts,\n useGlobalAccess: boolean,\n plVersion: string,\n): ConnectionInfo {\n return {\n plUser,\n plPassword,\n ports,\n useGlobalAccess,\n plVersion,\n };\n}\n\nexport function parseConnectionInfo(content: string): ConnectionInfo {\n return ConnectionInfo.parse(JSON.parse(content));\n}\n\nexport function stringifyConnectionInfo(conn: ConnectionInfo): string {\n return JSON.stringify(conn, undefined, 2);\n}\n","import type * as ssh from 'ssh2';\nimport { SshClient } from './ssh';\nimport type { MiLogger } from '@milaboratories/ts-helpers';\nimport { sleep, notEmpty } from '@milaboratories/ts-helpers';\nimport type { DownloadBinaryResult } from '../common/pl_binary_download';\nimport { downloadBinaryNoExtract } from '../common/pl_binary_download';\nimport upath from 'upath';\nimport * as plpath from './pl_paths';\nimport { getDefaultPlVersion } from '../common/pl_version';\n\nimport net from 'net';\nimport type { PlLicenseMode, SshPlConfigGenerationResult } from '@milaboratories/pl-config';\nimport { generateSshPlConfigs, getFreePort } from '@milaboratories/pl-config';\nimport type { SupervisorStatus } from './supervisord';\nimport { supervisorStatus, supervisorStop as supervisorCtlShutdown, generateSupervisordConfig, supervisorCtlStart } from './supervisord';\nimport type { ConnectionInfo, SshPlPorts } from './connection_info';\nimport { newConnectionInfo, parseConnectionInfo, stringifyConnectionInfo } from './connection_info';\nimport type { PlBinarySourceDownload } from '../common/pl_binary';\n\nexport class SshPl {\n private initState: PlatformaInitState = {};\n constructor(\n public readonly logger: MiLogger,\n public readonly sshClient: SshClient,\n private readonly username: string,\n ) { }\n\n public info() {\n return {\n username: this.username,\n initState: this.initState,\n };\n }\n\n public static async init(logger: MiLogger, config: ssh.ConnectConfig): Promise<SshPl> {\n try {\n const sshClient = await SshClient.init(logger, config);\n return new SshPl(logger, sshClient, notEmpty(config.username));\n } catch (e: unknown) {\n logger.error(`Connection error in SshClient.init: ${e}`);\n throw e;\n }\n }\n\n public cleanUp() {\n this.sshClient.close();\n }\n\n /** Provides an info if the platforma and minio are running along with the debug info. */\n public async isAlive(): Promise<SupervisorStatus> {\n const arch = await this.getArch();\n const remoteHome = await this.getUserHomeDirectory();\n return await supervisorStatus(this.logger, this.sshClient, remoteHome, arch.arch);\n }\n\n /** Starts all the services on the server.\n * Idempotent semantic: we could call it several times. */\n public async start() {\n const arch = await this.getArch();\n const remoteHome = await this.getUserHomeDirectory();\n\n try {\n if (!(await this.isAlive()).allAlive) {\n await supervisorCtlStart(this.sshClient, remoteHome, arch.arch);\n\n // We are waiting for Platforma to run to ensure that it has started.\n return await this.checkIsAliveWithInterval();\n }\n } catch (e: unknown) {\n const msg = `SshPl.start: error occurred ${e}`;\n this.logger.error(msg);\n throw new Error(msg);\n }\n }\n\n /** Stops all the services on the server.\n * Idempotent semantic: we could call it several times. */\n public async stop() {\n const arch = await this.getArch();\n const remoteHome = await this.getUserHomeDirectory();\n\n try {\n if ((await this.isAlive()).allAlive) {\n await supervisorCtlShutdown(this.sshClient, remoteHome, arch.arch);\n return await this.checkIsAliveWithInterval(undefined, undefined, false);\n }\n } catch (e: unknown) {\n const msg = `PlSsh.stop: error occurred ${e}`;\n this.logger.error(msg);\n throw new Error(msg);\n }\n }\n\n /** Stops the services, deletes a directory with the state and closes SSH connection. */\n public async reset(): Promise<boolean> {\n await this.stopAndClean();\n this.cleanUp();\n return true;\n }\n\n /** Stops platforma and deletes its state. */\n public async stopAndClean(): Promise<void> {\n const remoteHome = await this.getUserHomeDirectory();\n\n this.logger.info(`pl.reset: Stop Platforma on the server`);\n await this.stop();\n\n this.logger.info(`pl.reset: Deleting Platforma workDir ${plpath.workDir(remoteHome)} on the server`);\n await this.sshClient.deleteFolder(plpath.workDir(remoteHome));\n }\n\n /** Downloads binaries and untar them on the server,\n * generates all the configs, creates necessary dirs,\n * and finally starts all the services. */\n public async platformaInit(options: SshPlConfig): Promise<ConnectionInfo> {\n const state: PlatformaInitState = { localWorkdir: options.localWorkdir };\n\n try {\n // merge options with default ops.\n const ops: SshPlConfig = {\n ...defaultSshPlConfig,\n ...options,\n };\n state.plBinaryOps = ops.plBinary;\n state.arch = await this.getArch();\n state.remoteHome = await this.getUserHomeDirectory();\n state.alive = await this.isAlive();\n\n if (state.alive.allAlive) {\n state.userCredentials = await this.getUserCredentials(state.remoteHome);\n if (!state.userCredentials) {\n throw new Error(`SshPl.platformaInit: platforma is alive but userCredentials are not found`);\n }\n const sameGA = state.userCredentials.useGlobalAccess == ops.useGlobalAccess;\n const samePlVersion = state.userCredentials.plVersion == ops.plBinary!.version;\n state.needRestart = !(sameGA && samePlVersion);\n this.logger.info(`SshPl.platformaInit: need restart? ${state.needRestart}`);\n\n if (!state.needRestart)\n return state.userCredentials;\n\n await this.stop();\n }\n\n const downloadRes = await this.downloadBinariesAndUploadToTheServer(\n ops.localWorkdir, ops.plBinary!, state.remoteHome, state.arch,\n );\n state.binPaths = { ...downloadRes, history: undefined };\n state.downloadedBinaries = downloadRes.history;\n\n state.ports = await this.fetchPorts(state.remoteHome, state.arch);\n\n if (!state.ports.debug.remote || !state.ports.grpc.remote || !state.ports.minioPort.remote || !state.ports.minioConsolePort.remote || !state.ports.monitoring.remote) {\n throw new Error(`SshPl.platformaInit: remote ports are not defined`);\n }\n\n const config = await generateSshPlConfigs({\n logger: this.logger,\n workingDir: plpath.workDir(state.remoteHome),\n portsMode: {\n type: 'customWithMinio',\n ports: {\n debug: state.ports.debug.remote,\n grpc: state.ports.grpc.remote,\n minio: state.ports.minioPort.remote,\n minioConsole: state.ports.minioConsolePort.remote,\n monitoring: state.ports.monitoring.remote,\n\n grpcLocal: state.ports.grpc.local,\n minioLocal: state.ports.minioPort.local,\n },\n },\n licenseMode: ops.license,\n useGlobalAccess: notEmpty(ops.useGlobalAccess),\n });\n state.generatedConfig = { ...config, filesToCreate: { skipped: 'it is too wordy' } };\n\n for (const [filePath, content] of Object.entries(config.filesToCreate)) {\n await this.sshClient.writeFileOnTheServer(filePath, content);\n this.logger.info(`Created file ${filePath}`);\n }\n\n for (const dir of config.dirsToCreate) {\n await this.sshClient.ensureRemoteDirCreated(dir);\n this.logger.info(`Created directory ${dir}`);\n }\n\n const supervisorConfig = generateSupervisordConfig(\n config.minioConfig.storageDir,\n config.minioConfig.envs,\n await this.getFreePortForPlatformaOnServer(state.remoteHome, state.arch),\n config.workingDir,\n config.plConfig.configPath,\n state.binPaths.minioRelPath,\n state.binPaths.downloadedPl,\n );\n\n const writeResult = await this.sshClient.writeFileOnTheServer(plpath.supervisorConf(state.remoteHome), supervisorConfig);\n if (!writeResult) {\n throw new Error(`Can not write supervisord config on the server ${plpath.workDir(state.remoteHome)}`);\n }\n\n state.connectionInfo = newConnectionInfo(\n config.plUser,\n config.plPassword,\n state.ports,\n notEmpty(ops.useGlobalAccess),\n ops.plBinary!.version,\n );\n await this.sshClient.writeFileOnTheServer(\n plpath.connectionInfo(state.remoteHome),\n stringifyConnectionInfo(state.connectionInfo),\n );\n\n await this.start();\n state.started = true;\n this.initState = state;\n\n return state.connectionInfo;\n } catch (e: unknown) {\n const msg = `SshPl.platformaInit: error occurred: ${e}, state: ${JSON.stringify(state)}`;\n this.logger.error(msg);\n\n throw new Error(msg);\n }\n }\n\n public async downloadBinariesAndUploadToTheServer(\n localWorkdir: string,\n plBinary: PlBinarySourceDownload,\n remoteHome: string,\n arch: Arch,\n ) {\n const state: DownloadAndUntarState[] = [];\n try {\n const pl = await this.downloadAndUntar(\n localWorkdir, remoteHome, arch,\n 'pl', `pl-${plBinary.version}`,\n );\n state.push(pl);\n\n const supervisor = await this.downloadAndUntar(\n localWorkdir, remoteHome, arch,\n 'supervisord', plpath.supervisordDirName,\n );\n state.push(supervisor);\n\n const minioPath = plpath.minioBin(remoteHome, arch.arch);\n const minio = await this.downloadAndUntar(\n localWorkdir, remoteHome, arch,\n 'minio', plpath.minioDirName,\n );\n state.push(minio);\n await this.sshClient.chmod(minioPath, 0o750);\n\n return {\n history: state,\n minioRelPath: minioPath,\n downloadedPl: plpath.platformaBin(remoteHome, arch.arch),\n };\n } catch (e: unknown) {\n const msg = `SshPl.downloadBinariesAndUploadToServer: error ${e} occurred, state: ${JSON.stringify(state)}`;\n this.logger.error(msg);\n throw e;\n }\n }\n\n /** We have to extract pl in the remote server,\n * because Windows doesn't support symlinks\n * that are found in Linux pl binaries tgz archive.\n * For this reason, we extract all to the remote server.\n * It requires `tar` to be installed on the server\n * (it's not installed for Rocky Linux for example). */\n public async downloadAndUntar(\n localWorkdir: string,\n remoteHome: string,\n arch: Arch,\n softwareName: string,\n tgzName: string,\n ): Promise<DownloadAndUntarState> {\n const state: DownloadAndUntarState = {};\n state.binBasePath = plpath.binariesDir(remoteHome);\n await this.sshClient.ensureRemoteDirCreated(state.binBasePath);\n state.binBasePathCreated = true;\n\n let downloadBinaryResult: DownloadBinaryResult | null = null;\n const attempts = 5;\n for (let i = 1; i <= attempts; i++) {\n try {\n downloadBinaryResult = await downloadBinaryNoExtract(\n this.logger,\n localWorkdir,\n softwareName,\n tgzName,\n arch.arch, arch.platform,\n );\n break;\n } catch (e: unknown) {\n await sleep(300);\n if (i == attempts) {\n throw new Error(`downloadAndUntar: ${attempts} attempts, last error: ${e}`);\n }\n }\n }\n state.downloadResult = notEmpty(downloadBinaryResult);\n\n state.localArchivePath = upath.resolve(state.downloadResult.archivePath);\n state.remoteDir = upath.join(state.binBasePath, state.downloadResult.baseName);\n state.remoteArchivePath = state.remoteDir + '.tgz';\n\n await this.sshClient.ensureRemoteDirCreated(state.remoteDir);\n await this.sshClient.uploadFile(state.localArchivePath, state.remoteArchivePath);\n state.uploadDone = true;\n\n // TODO: Create a proper archive to avoid xattr warnings\n const untarResult = await this.sshClient.exec(\n `tar --warning=no-all -xvf ${state.remoteArchivePath} --directory=${state.remoteDir}`,\n );\n\n if (untarResult.stderr)\n throw Error(`downloadAndUntar: untar: stderr occurred: ${untarResult.stderr}, stdout: ${untarResult.stdout}`);\n\n state.untarDone = true;\n\n return state;\n }\n\n public async needDownload(remoteHome: string, arch: Arch) {\n const checkPathSupervisor = plpath.supervisorBin(remoteHome, arch.arch);\n const checkPathMinio = plpath.minioDir(remoteHome, arch.arch);\n const checkPathPlatforma = plpath.platformaBin(remoteHome, arch.arch);\n\n if (!await this.sshClient.checkFileExists(checkPathPlatforma)\n || !await this.sshClient.checkFileExists(checkPathMinio)\n || !await this.sshClient.checkFileExists(checkPathSupervisor)) {\n return true;\n }\n\n return false;\n }\n\n public async checkIsAliveWithInterval(interval: number = 1000, count = 15, shouldStart = true): Promise<void> {\n const maxMs = count * interval;\n\n let total = 0;\n let alive = await this.isAlive();\n while (shouldStart ? !alive.allAlive : alive.allAlive) {\n await sleep(interval);\n total += interval;\n if (total > maxMs) {\n throw new Error(`isAliveWithInterval: The process did not ${shouldStart ? 'started' : 'stopped'} after ${maxMs} ms. Live status: ${JSON.stringify(alive)}`);\n }\n alive = await this.isAlive();\n }\n }\n\n public async getUserCredentials(remoteHome: string): Promise<ConnectionInfo> {\n const connectionInfo = await this.sshClient.readFile(plpath.connectionInfo(remoteHome));\n return parseConnectionInfo(connectionInfo);\n }\n\n public async fetchPorts(remoteHome: string, arch: Arch): Promise<SshPlPorts> {\n const ports: SshPlPorts = {\n grpc: {\n local: await getFreePort(),\n remote: await this.getFreePortForPlatformaOnServer(remoteHome, arch),\n },\n monitoring: {\n local: await getFreePort(),\n remote: await this.getFreePortForPlatformaOnServer(remoteHome, arch),\n },\n debug: {\n local: await getFreePort(),\n remote: await this.getFreePortForPlatformaOnServer(remoteHome, arch),\n },\n minioPort: {\n local: await getFreePort(),\n remote: await this.getFreePortForPlatformaOnServer(remoteHome, arch),\n },\n minioConsolePort: {\n local: await getFreePort(),\n remote: await this.getFreePortForPlatformaOnServer(remoteHome, arch),\n },\n };\n\n return ports;\n }\n\n public async getLocalFreePort(): Promise<number> {\n return new Promise((res) => {\n const srv = net.createServer();\n srv.listen(0, () => {\n const port = (srv.address() as net.AddressInfo).port;\n srv.close((_) => res(port));\n });\n });\n }\n\n public async getFreePortForPlatformaOnServer(remoteHome: string, arch: Arch): Promise<number> {\n const freePortBin = plpath.platformaFreePortBin(remoteHome, arch.arch);\n\n const { stdout, stderr } = await this.sshClient.exec(`${freePortBin}`);\n if (stderr) {\n throw new Error(`getFreePortForPlatformaOnServer: stderr is not empty: ${stderr}, stdout: ${stdout}`);\n }\n\n return +stdout;\n }\n\n public async getArch(): Promise<Arch> {\n const { stdout, stderr } = await this.sshClient.exec('uname -s && uname -m');\n if (stderr)\n throw new Error(`getArch: stderr is not empty: ${stderr}, stdout: ${stdout}`);\n\n const arr = stdout.split('\\n');\n\n return {\n platform: arr[0],\n arch: arr[1],\n };\n }\n\n public async getUserHomeDirectory() {\n const { stdout, stderr } = await this.sshClient.exec('echo $HOME');\n\n if (stderr) {\n const home = `/home/${this.username}`;\n console.warn(`getUserHomeDirectory: stderr is not empty: ${stderr}, stdout: ${stdout}, will get a default home: ${home}`);\n\n return home;\n }\n\n return stdout.trim();\n }\n}\n\ntype Arch = { platform: string; arch: string };\n\nexport type SshPlConfig = {\n localWorkdir: string;\n license: PlLicenseMode;\n useGlobalAccess?: boolean;\n plBinary?: PlBinarySourceDownload;\n};\n\nconst defaultSshPlConfig: Pick<\n SshPlConfig,\n | 'useGlobalAccess'\n | 'plBinary'\n> = {\n useGlobalAccess: false,\n plBinary: {\n type: 'Download',\n version: getDefaultPlVersion(),\n },\n};\n\ntype BinPaths = {\n history?: DownloadAndUntarState[];\n minioRelPath: string;\n downloadedPl: string;\n};\n\ntype DownloadAndUntarState = {\n binBasePath?: string;\n binBasePathCreated?: boolean;\n downloadResult?: DownloadBinaryResult;\n attempts?: number;\n\n localArchivePath?: string;\n remoteDir?: string;\n remoteArchivePath?: string;\n uploadDone?: boolean;\n untarDone?: boolean;\n};\n\ntype PlatformaInitState = {\n localWorkdir?: string;\n plBinaryOps?: PlBinarySourceDownload;\n arch?: Arch;\n remoteHome?: string;\n alive?: SupervisorStatus;\n userCredentials?: ConnectionInfo;\n needRestart?: boolean;\n downloadedBinaries?: DownloadAndUntarState[];\n binPaths?: BinPaths;\n ports?: SshPlPorts;\n generatedConfig?: SshPlConfigGenerationResult;\n connectionInfo?: ConnectionInfo;\n started?: boolean;\n};\n"],"names":["processRun","logger","opts","spawn","isProcessAlive","pid","processStop","processWaitStopped","maxMs","total","sleep","OSes","newOs","osName","Arches","newArch","arch","cdn","gaCdn","downloadBinaryNoExtract","baseDir","softwareName","tgzName","platform","getPathsForDownload","archiveUrl","alternativeArchiveGAUrl","archivePath","downloadArchive","downloadBinary","archiveName","archiveType","targetFolder","baseName","extractArchive","os","osToArchiveType","archiveFileName","upath","dstArchiveFile","state","fileExists","fsp","body","statusCode","request","textBody","text","Readable","Writable","fs","e","msg","MarkerFileName","dstFolder","markerFilePath","tar","decompress","assertNever","getDefaultPlVersion","newDefaultPlBinarySource","resolveLocalPlBinaryPath","downloadDir","src","ops","osToBinaryName","filePid","dir","readPid","filePath","writePid","newTrace","trace","t","k","v","withTrace","fn","LocalConfigYaml","LocalPl","workingDir","startOptions","initialStartHistory","onClose","onError","onCloseAndError","onCloseAndErrorNoStop","__publicField","instance","pidFile","notEmpty","localPlatformaInit","_ops","workDir","localPlatformaReadPidAndStop","configPath","plBinPath","baseBinaryPath","processOpts","pl","file","oldPid","alive","defaultConfig","SshClient","client","config","withDefaults","Client","_a","_b","connect","command","resolve","reject","err","stream","stdout","stderr","code","data","host","port","conn","types","log","match","method","ports","randomBytes","persistentClient","RetryablePromise","p","server","net","localSocket","sockLog","forwardOut","s","rawAddress","address","hostname","dns","privateKey","ssh","localPath","remotePath","sftp","newErr","callback","mode","items","item","itemPath","error","path","list","a","b","directoryPath","message","buffer","stats","readFile","result","localDir","remoteDir","files","directories","currentPath","createNext","index","directory","localHost","localPort","remoteHost","remotePort","minioDirName","supervisordDirName","supervisordSubDirName","remoteHome","binariesDir","platformaBaseDir","platformaDir","platformaBin","platformaFreePortBin","minioDir","minioBin","supervisorBinDir","supervisorBin","supervisorConf","connectionInfo","supervisorCtlStart","sshClient","supervisorExec","supervisorStop","supervisorStatus","platforma","isProgramRunning","minio","status","generateSupervisordConfig","minioStorageDir","minioEnvs","supervisorRemotePort","remoteWorkDir","platformaConfigPath","minioPath","plPath","minioEnvStr","key","value","password","freePort","supervisorCmd","plpath.supervisorBin","plpath.supervisorConf","cmd","output","programName","str","line","name","PortPair","z","SshPlPorts","ConnectionInfo","newConnectionInfo","plUser","plPassword","useGlobalAccess","plVersion","parseConnectionInfo","content","stringifyConnectionInfo","SshPl","username","supervisorCtlShutdown","plpath.workDir","options","defaultSshPlConfig","sameGA","samePlVersion","downloadRes","generateSshPlConfigs","supervisorConfig","plpath.connectionInfo","localWorkdir","plBinary","supervisor","plpath.supervisordDirName","plpath.minioBin","plpath.minioDirName","plpath.platformaBin","plpath.binariesDir","downloadBinaryResult","attempts","i","untarResult","checkPathSupervisor","checkPathMinio","plpath.minioDir","checkPathPlatforma","interval","count","shouldStart","getFreePort","res","srv","_","freePortBin","plpath.platformaFreePortBin","arr","home"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","sources":["../src/local/process.ts","../src/common/os_and_arch.ts","../src/common/pl_binary_download.ts","../src/common/pl_version.ts","../src/common/pl_binary.ts","../src/local/pid.ts","../src/local/trace.ts","../src/local/pl.ts","../src/ssh/ssh.ts","../src/ssh/pl_paths.ts","../src/ssh/supervisord.ts","../src/ssh/connection_info.ts","../src/ssh/pl.ts"],"sourcesContent":["import type { SpawnOptions, ChildProcess } from 'child_process';\nimport { spawn } from 'child_process';\nimport type { MiLogger } from '@milaboratories/ts-helpers';\nimport { sleep } from '@milaboratories/ts-helpers';\n\nexport type ProcessOptions = {\n cmd: string;\n args: string[];\n opts: SpawnOptions;\n};\n\nexport function processRun(logger: MiLogger, opts: ProcessOptions): ChildProcess {\n logger.info(`Running:\ncmd: ${JSON.stringify([opts.cmd, ...opts.args])}\nwd: ${opts.opts.cwd}`);\n\n logger.info(' spawning child process');\n return spawn(opts.cmd, opts.args, opts.opts);\n}\n\nexport async function isProcessAlive(pid: number) {\n try {\n process.kill(pid, 0);\n return true;\n } catch (e) {\n return false;\n }\n}\n\nexport function processStop(pid: number) {\n return process.kill(pid, 'SIGINT');\n}\n\nexport async function processWaitStopped(pid: number, maxMs: number) {\n const sleepMs = 100;\n let total = 0;\n while (await isProcessAlive(pid)) {\n await sleep(sleepMs);\n total += sleepMs;\n if (total > maxMs) {\n throw new Error(`The process did not stopped after ${maxMs} ms.`);\n }\n }\n}\n","import os from 'os';\n\nexport const OSes = ['linux', 'macos', 'windows'] as const;\nexport type OSType = (typeof OSes)[number];\n\n/** @param osName - should be the thing returned from either {@link os.platform())} or `uname -s` */\nexport function newOs(osName: string): OSType {\n switch (osName.toLowerCase()) {\n case 'darwin':\n return 'macos';\n case 'linux':\n return 'linux';\n case 'win32':\n return 'windows';\n default:\n throw new Error(\n `operating system '${osName}' is not currently supported by Platforma ecosystem. The list of OSes supported: `\n + JSON.stringify(OSes),\n );\n }\n}\n\nexport const Arches = ['amd64', 'arm64'] as const;\nexport type ArchType = (typeof Arches)[number];\n\n/** @param arch - should be the thing returned from either {@link os.arch())} or `uname -m` */\nexport function newArch(arch: string): ArchType {\n switch (arch) {\n case 'aarch64':\n case 'aarch64_be':\n case 'arm64':\n return 'arm64';\n\n case 'x86_64':\n case 'x64':\n return 'amd64';\n\n default:\n throw new Error(\n `processor architecture '${arch}' is not currently supported by Platforma ecosystem. The list of architectures supported: `\n + JSON.stringify(Arches),\n );\n }\n}\n","import fs from 'fs';\nimport fsp from 'fs/promises';\nimport upath from 'upath';\nimport { request } from 'undici';\nimport { Writable, Readable } from 'stream';\nimport { text } from 'stream/consumers';\nimport * as tar from 'tar';\nimport type { MiLogger } from '@milaboratories/ts-helpers';\nimport { assertNever, fileExists } from '@milaboratories/ts-helpers';\nimport decompress from 'decompress';\nimport type { ArchType, OSType } from './os_and_arch';\nimport { newOs, newArch } from './os_and_arch';\n\nconst cdn = 'https://cdn.platforma.bio/software';\n// We'll download things from Global Access if downloading from CDN has failed\n// (it might be that it's blocked from the company's network.)\nconst gaCdn = 'https://cdn-ga.pl-open.science/software';\n\nexport type DownloadBinaryResult = {\n archiveUrl: string;\n alternativeArchiveGAUrl: string;\n wasDownloadedFrom?: string;\n archivePath: string;\n archiveType: ArchiveType;\n targetFolder: string;\n baseName: string;\n};\n\nexport async function downloadBinaryNoExtract(\n logger: MiLogger,\n baseDir: string,\n softwareName: string,\n tgzName: string,\n arch: string,\n platform: string,\n): Promise<DownloadBinaryResult> {\n const opts = getPathsForDownload(softwareName, tgzName, baseDir, newArch(arch), newOs(platform));\n const { archiveUrl, alternativeArchiveGAUrl, archivePath } = opts;\n\n try {\n await downloadArchive(logger, archiveUrl, archivePath);\n opts.wasDownloadedFrom = archiveUrl;\n } catch (e: unknown) {\n await downloadArchive(logger, alternativeArchiveGAUrl, archivePath);\n opts.wasDownloadedFrom = alternativeArchiveGAUrl;\n }\n\n return opts;\n}\n\nexport async function downloadBinary(\n logger: MiLogger,\n baseDir: string,\n softwareName: string,\n archiveName: string,\n arch: string,\n platform: string,\n): Promise<DownloadBinaryResult> {\n const opts = getPathsForDownload(softwareName, archiveName, baseDir, newArch(arch), newOs(platform));\n const { archiveUrl, alternativeArchiveGAUrl, archivePath, archiveType, targetFolder, baseName } = opts;\n\n try {\n await downloadArchive(logger, archiveUrl, archivePath);\n opts.wasDownloadedFrom = archiveUrl;\n } catch (e: unknown) {\n await downloadArchive(logger, alternativeArchiveGAUrl, archivePath);\n opts.wasDownloadedFrom = alternativeArchiveGAUrl;\n }\n\n await extractArchive(logger, archivePath, archiveType, targetFolder);\n\n return opts;\n}\n\nfunction getPathsForDownload(\n softwareName: string,\n archiveName: string,\n baseDir: string,\n arch: ArchType,\n os: OSType,\n): DownloadBinaryResult {\n const baseName = `${archiveName}-${arch}`;\n const archiveType = osToArchiveType[os];\n\n const archiveFileName = `${baseName}.${archiveType}`;\n const archiveUrl = `${cdn}/${softwareName}/${os}/${archiveFileName}`;\n const alternativeArchiveGAUrl = `${gaCdn}/${softwareName}/${os}/${archiveFileName}`;\n const archivePath = upath.join(baseDir, archiveFileName);\n // folder where binary distribution of pl will be unpacked\n const targetFolder = upath.join(baseDir, baseName);\n\n return {\n archiveUrl,\n alternativeArchiveGAUrl,\n archivePath,\n archiveType,\n targetFolder,\n baseName,\n };\n}\n\nexport type DownloadInfo = {\n dstArchive?: string;\n fileExisted?: boolean;\n dirnameCreated?: boolean;\n statusCode?: number;\n errorMsg?: string;\n tmpPath?: string;\n wroteTmp?: boolean;\n tmpExisted?: boolean;\n renamed?: boolean;\n newExisted?: boolean;\n};\n\nexport async function downloadArchive(\n logger: MiLogger, archiveUrl: string, dstArchiveFile: string,\n): Promise<DownloadInfo> {\n const state: DownloadInfo = {};\n state.dstArchive = dstArchiveFile;\n\n try {\n state.fileExisted = await fileExists(dstArchiveFile);\n if (state.fileExisted) {\n logger.info(`Platforma Backend archive download skipped: '${dstArchiveFile}' already exists`);\n return state;\n }\n\n await fsp.mkdir(upath.dirname(dstArchiveFile), { recursive: true });\n state.dirnameCreated = true;\n\n logger.info(`Downloading archive:\\n URL: ${archiveUrl}\\n Save to: ${dstArchiveFile}`);\n\n const { body, statusCode } = await request(archiveUrl);\n state.statusCode = statusCode;\n if (statusCode != 200) {\n // completely draining the stream to prevent leaving open connections\n const textBody = await text(body);\n state.errorMsg = `failed to download archive: ${statusCode}, response: ${textBody.slice(0, 1000)}`;\n logger.error(state.errorMsg);\n throw new Error(state.errorMsg);\n }\n\n // to prevent incomplete downloads we first write in a temp file\n state.tmpPath = dstArchiveFile + '.tmp';\n await Readable.toWeb(body).pipeTo(Writable.toWeb(fs.createWriteStream(state.tmpPath)));\n state.wroteTmp = true;\n state.tmpExisted = await fileExists(state.tmpPath);\n\n // and then atomically rename it\n await fsp.rename(state.tmpPath, dstArchiveFile);\n state.renamed = true;\n state.newExisted = await fileExists(dstArchiveFile);\n\n return state;\n } catch (e: unknown) {\n const msg = `downloadArchive: error ${JSON.stringify(e)} occurred, state: ${JSON.stringify(state)}`;\n logger.error(msg);\n throw new Error(msg);\n }\n}\n\n/** Used to prevent mid-way interrupted unarchived folders to be used */\nconst MarkerFileName = '.ok';\n\nexport async function extractArchive(\n logger: MiLogger,\n archivePath: string,\n archiveType: ArchiveType,\n dstFolder: string,\n) {\n logger.info('extracting archive...');\n logger.info(` archive path: '${archivePath}'`);\n logger.info(` target dir: '${dstFolder}'`);\n\n if (!(await fileExists(archivePath))) {\n const msg = `Platforma Backend binary archive not found at '${archivePath}'`;\n logger.error(msg);\n throw new Error(msg);\n }\n\n const markerFilePath = upath.join(dstFolder, MarkerFileName);\n\n if (await fileExists(markerFilePath)) {\n logger.info(`Platforma Backend binaries unpack skipped: '${dstFolder}' exists`);\n return;\n }\n\n if (await fileExists(dstFolder)) {\n logger.info(`Removing previous incompletely unpacked folder: '${dstFolder}'`);\n await fsp.rm(dstFolder, { recursive: true });\n }\n\n logger.info(` creating target dir '${dstFolder}'`);\n await fsp.mkdir(dstFolder, { recursive: true });\n\n logger.info(\n `Unpacking Platforma Backend archive:\\n Archive: ${archivePath}\\n Target dir: ${dstFolder}`,\n );\n\n switch (archiveType) {\n case 'tgz':\n await tar.x({\n file: archivePath,\n cwd: dstFolder,\n gzip: true,\n });\n break;\n\n case 'zip':\n await decompress(archivePath, dstFolder);\n break;\n\n default:\n assertNever(archiveType);\n }\n\n // writing marker file, to be able in the future detect that we completely unarchived the tar file\n await fsp.writeFile(markerFilePath, 'ok');\n\n logger.info(` ... unpack done.`);\n}\n\nexport type ArchiveType = 'tgz' | 'zip';\n\nconst osToArchiveType: Record<OSType, ArchiveType> = {\n linux: 'tgz',\n macos: 'tgz',\n windows: 'zip',\n};\n","declare const PL_VERSION: string;\n\nexport function getDefaultPlVersion(): string {\n return PL_VERSION;\n}\n","import type { MiLogger } from '@milaboratories/ts-helpers';\nimport { assertNever } from '@milaboratories/ts-helpers';\nimport { downloadBinary } from './pl_binary_download';\nimport { getDefaultPlVersion } from './pl_version';\nimport os from 'os';\nimport upath from 'upath';\nimport type { OSType } from './os_and_arch';\nimport { newOs } from './os_and_arch';\n\n/** Shows how the binary should be got. */\nexport type PlBinarySource = PlBinarySourceDownload | PlBinarySourceLocal;\n\nexport type PlBinarySourceDownload = {\n readonly type: 'Download';\n readonly version: string;\n};\n\nexport type PlBinarySourceLocal = {\n readonly type: 'Local';\n readonly path: string;\n};\n\nexport function newDefaultPlBinarySource(): PlBinarySourceDownload {\n return { type: 'Download', version: getDefaultPlVersion() };\n}\n\nexport async function resolveLocalPlBinaryPath(\n logger: MiLogger,\n downloadDir: string,\n src: PlBinarySource,\n): Promise<string> {\n switch (src.type) {\n case 'Download':\n const ops = await downloadBinary(logger, downloadDir, 'pl', `pl-${src.version}`, os.arch(), os.platform());\n return upath.join(ops.baseName, 'binaries', osToBinaryName[newOs(os.platform())]);\n\n case 'Local':\n return src.path;\n\n default:\n assertNever(src);\n }\n}\n\nexport const osToBinaryName: Record<OSType, string> = {\n linux: 'platforma',\n macos: 'platforma',\n windows: 'platforma.exe',\n};\n","import { fileExists } from '@milaboratories/ts-helpers';\nimport fs from 'fs/promises';\nimport upath from 'upath';\n\nexport function filePid(dir: string) {\n return upath.join(dir, 'pl_pid');\n}\n\nexport async function readPid(filePath: string): Promise<number | undefined> {\n if (!(await fileExists(filePath))) {\n return undefined;\n }\n\n const text = await fs.readFile(filePath);\n\n return Number(text.toString());\n}\n\nexport async function writePid(filePath: string, pid: number) {\n await fs.writeFile(filePath, JSON.stringify(pid));\n}\n","import type { MiLogger } from '@milaboratories/ts-helpers';\n\n/** Records all inputs and outputs of one's choice, so if the error happened\n * one can check how it was by just printing this structure. */\nexport type Trace = Record<string, any>;\n\nexport function newTrace(): Trace {\n return {};\n}\n\nexport function trace(t: Trace, k: string, v: any) {\n t[k] = v;\n return v;\n}\n\n/** Creates a trace and runs a function with it. The function can record all its\n * logs or traces using `trace` fn. */\nexport async function withTrace<T>(\n logger: MiLogger,\n fn: (trace: (k: string, v: any) => typeof v, t: Trace) => Promise<T>,\n): Promise<T> {\n const t = newTrace();\n try {\n const result = await fn((k: string, v: any) => trace(t, k, v), t);\n return result;\n } catch (e: any) {\n logger.error(`error ${e} while doing traced operation, state: ${JSON.stringify(t)}`);\n throw e;\n }\n}\n","import type {\n ProcessOptions } from './process';\nimport {\n isProcessAlive,\n processStop,\n processWaitStopped,\n processRun,\n} from './process';\nimport type { PlBinarySource } from '../common/pl_binary';\nimport { newDefaultPlBinarySource, resolveLocalPlBinaryPath } from '../common/pl_binary';\nimport type { MiLogger } from '@milaboratories/ts-helpers';\nimport { notEmpty } from '@milaboratories/ts-helpers';\nimport type { ChildProcess, SpawnOptions } from 'child_process';\nimport { filePid, readPid, writePid } from './pid';\nimport type { Trace } from './trace';\nimport { withTrace } from './trace';\nimport upath from 'upath';\nimport fsp from 'fs/promises';\nimport type { Required } from 'utility-types';\n\nexport const LocalConfigYaml = 'config-local.yaml';\n\n/**\n * Represents a local running pl-core,\n * and has methods to start, check if it's running, stop and wait for stopping it.\n * Also, a hook on pl-core closed can be provided.\n */\nexport class LocalPl {\n private instance?: ChildProcess;\n public pid?: number;\n private nRuns: number = 0;\n private lastRunHistory: Trace = {};\n private wasStopped = false;\n\n constructor(\n private readonly logger: MiLogger,\n private readonly workingDir: string,\n private readonly startOptions: ProcessOptions,\n private readonly initialStartHistory: Trace,\n private readonly onClose?: (pl: LocalPl) => Promise<void>,\n private readonly onError?: (pl: LocalPl) => Promise<void>,\n private readonly onCloseAndError?: (pl: LocalPl) => Promise<void>,\n private readonly onCloseAndErrorNoStop?: (pl: LocalPl) => Promise<void>,\n ) {}\n\n async start() {\n await withTrace(this.logger, async (trace, t) => {\n this.wasStopped = false;\n const instance = processRun(this.logger, this.startOptions);\n instance.on('error', (e: any) => {\n this.logger.error(\n `error '${e}', while running platforma, started opts: ${JSON.stringify(this.debugInfo())}`,\n );\n\n // keep in mind there are no awaits here, it will be asynchronous\n if (this.onError !== undefined) this.onError(this);\n if (this.onCloseAndError !== undefined) this.onCloseAndError(this);\n if (this.onCloseAndErrorNoStop !== undefined && !this.wasStopped)\n this.onCloseAndErrorNoStop(this);\n });\n instance.on('close', () => {\n this.logger.warn(`platforma was closed, started opts: ${JSON.stringify(this.debugInfo())}`);\n\n // keep in mind there are no awaits here, it will be asynchronous\n if (this.onClose !== undefined) this.onClose(this);\n if (this.onCloseAndError !== undefined) this.onCloseAndError(this);\n if (this.onCloseAndErrorNoStop !== undefined && !this.wasStopped)\n this.onCloseAndErrorNoStop(this);\n });\n\n trace('started', true);\n\n const pidFile = trace('pidFile', filePid(this.workingDir));\n trace('pid', notEmpty(instance.pid));\n trace('pidWritten', await writePid(pidFile, notEmpty(instance.pid)));\n\n this.nRuns++;\n this.instance = instance;\n this.pid = instance.pid;\n this.lastRunHistory = t;\n });\n }\n\n stop() {\n // TODO use this.instance to stop the process\n this.wasStopped = true;\n processStop(notEmpty(this.pid));\n }\n\n async waitStopped() {\n await processWaitStopped(notEmpty(this.pid), 15000);\n }\n\n stopped() {\n return this.wasStopped;\n }\n\n async isAlive(): Promise<boolean> {\n return await isProcessAlive(notEmpty(this.pid));\n }\n\n debugInfo() {\n return {\n lastRunHistory: this.lastRunHistory,\n nRuns: this.nRuns,\n pid: this.pid,\n workingDir: this.workingDir,\n initialStartHistory: this.initialStartHistory,\n wasStopped: this.wasStopped,\n };\n }\n}\n\n/** Options to start a local pl-core. */\nexport type LocalPlOptions = {\n /** From what directory start a process. */\n readonly workingDir: string;\n /** A string representation of yaml config. */\n readonly config: string;\n /** How to get a binary, download it or get an existing one (default: download latest version) */\n readonly plBinary?: PlBinarySource;\n /** Additional options for a process, environments, stdout, stderr etc. */\n readonly spawnOptions?: SpawnOptions;\n /**\n * If the previous pl-core was started from the same directory,\n * we can check if it's still running and then stop it before starting a new one.\n * (default: true)\n */\n readonly closeOld?: boolean;\n\n readonly onClose?: (pl: LocalPl) => Promise<void>;\n readonly onError?: (pl: LocalPl) => Promise<void>;\n readonly onCloseAndError?: (pl: LocalPl) => Promise<void>;\n readonly onCloseAndErrorNoStop?: (pl: LocalPl) => Promise<void>;\n};\n\ntype LocalPlOptionsFull = Required<LocalPlOptions, 'plBinary' | 'spawnOptions' | 'closeOld'>;\n\n/**\n * Starts pl-core, if the option was provided downloads a binary, reads license environments etc.\n */\nexport async function localPlatformaInit(logger: MiLogger, _ops: LocalPlOptions): Promise<LocalPl> {\n // filling-in default values\n const ops = {\n plBinary: newDefaultPlBinarySource(),\n spawnOptions: {},\n closeOld: true,\n ..._ops,\n } satisfies LocalPlOptionsFull;\n\n return await withTrace(logger, async (trace, t) => {\n trace('startOptions', { ...ops, config: 'too wordy' });\n\n const workDir = upath.resolve(ops.workingDir);\n\n if (ops.closeOld) {\n trace('closeOld', await localPlatformaReadPidAndStop(logger, workDir));\n }\n\n const configPath = upath.join(workDir, LocalConfigYaml);\n\n logger.info(`writing configuration '${configPath}'...`);\n await fsp.writeFile(configPath, ops.config);\n\n const plBinPath = upath.join(workDir, 'binaries');\n const baseBinaryPath = await resolveLocalPlBinaryPath(logger, plBinPath, ops.plBinary);\n\n const binaryPath = trace('binaryPath', upath.join('binaries', baseBinaryPath));\n\n const processOpts: ProcessOptions = {\n cmd: binaryPath,\n args: ['--config', configPath],\n opts: {\n env: { ...process.env },\n cwd: workDir,\n stdio: ['pipe', 'ignore', 'inherit'],\n windowsHide: true, // hide a terminal on Windows\n ...ops.spawnOptions,\n },\n };\n trace('processOpts', {\n cmd: processOpts.cmd,\n args: processOpts.args,\n cwd: processOpts.opts.cwd,\n });\n\n const pl = new LocalPl(\n logger,\n ops.workingDir,\n processOpts,\n t,\n ops.onClose,\n ops.onError,\n ops.onCloseAndError,\n ops.onCloseAndErrorNoStop,\n );\n await pl.start();\n\n return pl;\n });\n}\n\n/** Reads a pid of the old pl-core if it was started in the same working directory,\n * and closes it. */\nasync function localPlatformaReadPidAndStop(\n logger: MiLogger,\n workingDir: string,\n): Promise<Record<string, any>> {\n return await withTrace(logger, async (trace, t) => {\n const file = trace('pidFilePath', filePid(workingDir));\n\n const oldPid = trace('pid', await readPid(file));\n const alive = trace('wasAlive', await isProcessAlive(oldPid));\n\n if (oldPid !== undefined && alive) {\n trace('stopped', processStop(oldPid));\n trace('waitStopped', await processWaitStopped(oldPid, 10_000));\n }\n\n return t;\n });\n}\n","import type { ConnectConfig, ClientChannel, SFTPWrapper } from 'ssh2';\nimport ssh, { Client } from 'ssh2';\nimport net from 'net';\nimport dns from 'dns';\nimport fs from 'fs';\nimport { readFile } from 'fs/promises';\nimport upath from 'upath';\nimport { RetryablePromise, type MiLogger } from '@milaboratories/ts-helpers';\nimport { randomBytes } from 'crypto';\n\nconst defaultConfig: ConnectConfig = {\n keepaliveInterval: 60000,\n keepaliveCountMax: 10,\n};\n\nexport type SshAuthMethods = 'publickey' | 'password';\nexport type SshAuthMethodsResult = SshAuthMethods[];\nexport type SshDirContent = {\n files: string[];\n directories: string[];\n};\n\nexport class SshClient {\n private config?: ConnectConfig;\n public homeDir?: string;\n private forwardedServers: net.Server[] = [];\n\n constructor(\n private readonly logger: MiLogger,\n private readonly client: Client,\n ) {}\n\n /**\n * Initializes the SshClient and establishes a connection using the provided configuration.\n * @param config - The connection configuration object for the SSH client.\n * @returns A new instance of SshClient with an active connection.\n */\n public static async init(logger: MiLogger, config: ConnectConfig): Promise<SshClient> {\n const withDefaults = {\n ...defaultConfig,\n ...config,\n };\n\n const client = new SshClient(logger, new Client());\n await client.connect(withDefaults);\n\n return client;\n }\n\n public getForwardedServers() {\n return this.forwardedServers;\n }\n\n public getFullHostName() {\n return `${this.config?.host}:${this.config?.port}`;\n }\n\n public getUserName() {\n return this.config?.username;\n }\n\n /**\n * Connects to the SSH server using the specified configuration.\n * @param config - The connection configuration object for the SSH client.\n * @returns A promise that resolves when the connection is established or rejects on error.\n */\n public async connect(config: ConnectConfig) {\n this.config = config;\n return await connect(this.client, config, () => {}, () => {});\n }\n\n /**\n * Executes a command on the SSH server.\n * @param command - The command to execute on the remote server.\n * @returns A promise resolving with the command's stdout and stderr outputs.\n */\n public async exec(command: string): Promise<SshExecResult> {\n return new Promise((resolve, reject) => {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n this.client.exec(command, (err: any, stream: ClientChannel) => {\n if (err) {\n return reject(`ssh.exec: ${command}, error occurred: ${err}`);\n }\n\n let stdout = '';\n let stderr = '';\n\n stream.on('close', (code: number) => {\n if (code === 0) {\n resolve({ stdout, stderr });\n } else {\n reject(new Error(`Command ${command} exited with code ${code}`));\n }\n }).on('data', (data: ArrayBuffer) => {\n stdout += data.toString();\n }).stderr.on('data', (data: ArrayBuffer) => {\n stderr += data.toString();\n });\n });\n });\n }\n\n /**\n * Retrieves the supported authentication methods for a given host and port.\n * @param host - The hostname or IP address of the server.\n * @param port - The port number to connect to on the server.\n * @returns 'publickey' | 'password'[] A promise resolving with a list of supported authentication methods.\n */\n public static async getAuthTypes(host: string, port: number): Promise<SshAuthMethodsResult> {\n return new Promise((resolve) => {\n let stdout = '';\n const conn = new Client();\n\n conn.on('ready', () => {\n conn.end();\n const types = this.extractAuthMethods(stdout);\n resolve(types.length === 0 ? ['publickey', 'password'] : types as SshAuthMethodsResult);\n });\n\n conn.on('error', () => {\n conn.end();\n resolve(['publickey', 'password']);\n });\n\n conn.connect({\n host,\n port,\n username: new Date().getTime().toString(),\n debug: (err) => {\n stdout += `${err}\\n`;\n },\n });\n });\n }\n\n /**\n * Extracts authentication methods from debug logs.\n * @param log - The debug log output containing authentication information.\n * @returns An array of extracted authentication methods.\n */\n private static extractAuthMethods(log: string): string[] {\n const match = log.match(/Inbound: Received USERAUTH_FAILURE \\((.+)\\)/);\n return match && match[1] ? match[1].split(',').map((method) => method.trim()) : [];\n }\n\n /**\n * Sets up port forwarding between a remote port on the SSH server and a local port.\n * A new connection is used for this operation instead of an existing one.\n * @param ports - An object specifying the remote and local port configuration.\n * @param config - Optional connection configuration for the SSH client.\n * @returns { server: net.Server } A promise resolving with the created server instance.\n */\n public async forwardPort(ports: { remotePort: number; localPort: number; localHost?: string }, config?: ConnectConfig): Promise<{ server: net.Server }> {\n const log = `ssh.forward:${ports.localPort}:${ports.remotePort}.id_${randomBytes(1).toString('hex')}`;\n config = config ?? this.config;\n\n // we make this thing persistent so that if the connection\n // drops (it happened in the past because of lots of errors and forwardOut opened channels),\n // we'll recreate it here.\n const persistentClient = new RetryablePromise((p: RetryablePromise<Client>) => {\n return new Promise<Client>((resolve, reject) => {\n const client = new Client();\n\n client.on('ready', () => {\n this.logger.info(`${log}.client.ready`);\n resolve(client);\n });\n\n client.on('error', (err) => {\n this.logger.info(`${log}.client.error: ${err}`);\n p.reset();\n reject(err);\n });\n\n client.on('close', () => {\n this.logger.info(`${log}.client.closed`);\n p.reset();\n });\n\n client.connect(config!);\n });\n });\n\n await persistentClient.ensure(); // warm up a connection\n\n return new Promise((resolve, reject) => {\n const server = net.createServer({ pauseOnConnect: true }, async (localSocket) => {\n const sockLog = `${log}.sock_${randomBytes(1).toString('hex')}`;\n // this.logger.info(`${sockLog}.localSocket: start connection`);\n let conn: Client;\n try {\n conn = await persistentClient.ensure();\n } catch (e: unknown) {\n this.logger.info(`${sockLog}.persistentClient.catch: ${e}`);\n localSocket.end();\n return;\n }\n\n let stream: ClientChannel;\n try {\n stream = await forwardOut(this.logger, conn, '127.0.0.1', 0, '127.0.0.1', ports.remotePort);\n } catch (e: unknown) {\n this.logger.error(`${sockLog}.forwardOut.err: ${e}`);\n localSocket.end();\n return;\n }\n\n localSocket.pipe(stream);\n stream.pipe(localSocket);\n localSocket.resume();\n // this.logger.info(`${sockLog}.forwardOut: connected`);\n\n stream.on('error', (err: unknown) => {\n this.logger.error(`${sockLog}.stream.error: ${err}`);\n localSocket.end();\n stream.end();\n });\n stream.on('close', () => {\n // this.logger.info(`${sockLog}.stream.close: closed`);\n localSocket.end();\n stream.end();\n });\n localSocket.on('close', () => {\n this.logger.info(`${sockLog}.localSocket: closed`);\n localSocket.end();\n stream.end();\n });\n });\n\n server.listen(ports.localPort, '127.0.0.1', () => {\n this.logger.info(`${log}.server: started listening`);\n this.forwardedServers.push(server);\n resolve({ server });\n });\n\n server.on('error', (err) => {\n server.close();\n reject(new Error(`${log}.server: error: ${JSON.stringify(err)}`));\n });\n\n server.on('close', () => {\n this.logger.info(`${log}.server: closed ${JSON.stringify(ports)}`);\n this.forwardedServers = this.forwardedServers.filter((s) => s !== server);\n });\n });\n }\n\n public closeForwardedPorts(): void {\n this.logger.info('[SSH] Closing all forwarded ports...');\n this.forwardedServers.forEach((server) => {\n const rawAddress = server.address();\n if (rawAddress && typeof rawAddress !== 'string') {\n const address: net.AddressInfo = rawAddress;\n this.logger.info(`[SSH] Closing port forward for server ${address.address}:${address.port}`);\n }\n\n server.close();\n });\n this.forwardedServers = [];\n }\n\n /**\n * Checks if a specified host is available by performing a DNS lookup.\n * @param hostname - The hostname or IP address to check.\n * @returns A promise resolving with `true` if the host is reachable, otherwise `false`.\n */\n public static async checkHostAvailability(hostname: string): Promise<boolean> {\n return new Promise((resolve) => {\n dns.lookup(hostname, (err) => {\n resolve(!err);\n });\n });\n }\n\n /**\n * Determines whether a private key requires a passphrase for use.\n * @param privateKey - The private key content to check.\n * @returns A promise resolving with `true` if a passphrase is required, otherwise `false`.\n */\n public static async isPassphraseRequiredForKey(privateKey: string): Promise<boolean> {\n return new Promise((resolve, reject) => {\n try {\n const keyOrError = ssh.utils.parseKey(privateKey);\n if (keyOrError instanceof Error) {\n resolve(true);\n }\n return resolve(false);\n } catch (err: unknown) {\n console.log('Error parsing privateKey');\n reject(new Error(`ssh.isPassphraseRequiredForKey: err ${err}`));\n }\n });\n }\n\n /**\n * Uploads a local file to a remote server via SFTP.\n * This function creates new SFTP connection\n * @param localPath - The local file path.\n * @param remotePath - The remote file path on the server.\n * @returns A promise resolving with `true` if the file was successfully uploaded.\n */\n public async uploadFile(localPath: string, remotePath: string): Promise<boolean> {\n return await this.withSftp(async (sftp) => {\n return new Promise((resolve, reject) => {\n sftp.fastPut(localPath, remotePath, (err) => {\n if (err) {\n const newErr = new Error(\n `ssh.uploadFile: err: ${err}, localPath: ${localPath}, remotePath: ${remotePath}`);\n return reject(newErr);\n }\n resolve(true);\n });\n });\n });\n }\n\n public async withSftp<R>(callback: (sftp: SFTPWrapper) => Promise<R>): Promise<R> {\n return new Promise((resolve, reject) => {\n this.client.sftp((err, sftp) => {\n if (err) {\n return reject(new Error(`ssh.withSftp: sftp err: ${err}`));\n }\n\n callback(sftp)\n .then(resolve)\n .catch((err) => {\n reject(new Error(`ssh.withSftp.callback: err ${err}`));\n })\n .finally(() => {\n sftp?.end();\n });\n });\n });\n }\n\n public async writeFileOnTheServer(remotePath: string, data: string | Buffer, mode: number = 0o660) {\n return this.withSftp(async (sftp) => {\n return this.writeFile(sftp, remotePath, data, mode);\n });\n }\n\n public async getForderStructure(sftp: SFTPWrapper, remotePath: string, data: SshDirContent = { files: [], directories: [] }): Promise<SshDirContent> {\n return new Promise((resolve, reject) => {\n sftp.readdir(remotePath, async (err, items) => {\n if (err) {\n return reject(err);\n }\n\n for (const item of items) {\n const itemPath = `${remotePath}/${item.filename}`;\n if (item.attrs.isDirectory()) {\n data.directories.push(itemPath);\n try {\n await this.getForderStructure(sftp, itemPath, data);\n } catch (error) {\n return reject(error);\n }\n } else {\n data.files.push(itemPath);\n }\n }\n resolve(data);\n });\n });\n }\n\n public rmdir(sftp: SFTPWrapper, path: string) {\n return new Promise((resolve, reject) => {\n sftp.rmdir(path, (err) => err ? reject(err) : resolve(true));\n });\n }\n\n public unlink(sftp: SFTPWrapper, path: string) {\n return new Promise((resolve, reject) => {\n sftp.unlink(path, (err) => err ? reject(err) : resolve(true));\n });\n }\n\n public async deleteFolder(path: string) {\n return this.withSftp(async (sftp) => {\n try {\n const list = await this.getForderStructure(sftp, path);\n this.logger.info(`ssh.deleteFolder list of files and directories`);\n this.logger.info(`ssh.deleteFolder list of files: ${list.files}`);\n this.logger.info(`ssh.deleteFolder list of directories: ${list.directories}`);\n\n for (const filePath of list.files) {\n this.logger.info(`ssh.deleteFolder unlink file ${filePath}`);\n await this.unlink(sftp, filePath);\n }\n\n list.directories.sort((a, b) => b.length - a.length);\n\n for (const directoryPath of list.directories) {\n this.logger.info(`ssh.deleteFolder rmdir ${directoryPath}`);\n await this.rmdir(sftp, directoryPath);\n }\n\n await this.rmdir(sftp, path);\n return true;\n } catch (e: unknown) {\n this.logger.error(e);\n const message = e instanceof Error ? e.message : '';\n throw new Error(`ssh.deleteFolder: path: ${path}, message: ${message}`);\n }\n });\n }\n\n public async readFile(remotePath: string): Promise<string> {\n return this.withSftp(async (sftp) => {\n return new Promise((resolve, reject) => {\n sftp.readFile(remotePath, (err, buffer) => {\n if (err) {\n return reject(new Error(`ssh.readFile: err occurred ${err}`));\n }\n resolve(buffer.toString());\n });\n });\n });\n }\n\n async chmod(path: string, mode: number) {\n return this.withSftp(async (sftp) => {\n return new Promise((resolve, reject) => {\n sftp.chmod(path, mode, (err) => {\n if (err) {\n return reject(new Error(`ssh.chmod: ${err}, path: ${path}, mode: ${mode}`));\n }\n return resolve(undefined);\n });\n });\n });\n }\n\n async checkFileExists(remotePath: string) {\n return this.withSftp(async (sftp) => {\n return new Promise((resolve, reject) => {\n sftp.stat(remotePath, (err: Error | undefined, stats) => {\n if (err) {\n if ((err as unknown as { code?: number })?.code === 2) {\n return resolve(false);\n }\n return reject(new Error(`ssh.checkFileExists: err ${err}`));\n }\n resolve(stats.isFile());\n });\n });\n });\n }\n\n async checkPathExists(remotePath: string): Promise<{ exists: boolean; isFile: boolean; isDirectory: boolean }> {\n return this.withSftp(async (sftp) => {\n return new Promise((resolve, reject) => {\n sftp.stat(remotePath, (err, stats) => {\n if (err) {\n if ((err as Error & { code: number }).code === 2) {\n return resolve({ exists: false, isFile: false, isDirectory: false });\n }\n return reject(new Error(`ssh.checkPathExists: ${err}`));\n }\n resolve({\n exists: true,\n isFile: stats.isFile(),\n isDirectory: stats.isDirectory(),\n });\n });\n });\n });\n }\n\n private async writeFile(sftp: SFTPWrapper, remotePath: string, data: string | Buffer, mode: number = 0o660): Promise<boolean> {\n return new Promise((resolve, reject) => {\n sftp.writeFile(remotePath, data, { mode }, (err) => {\n if (err) {\n return reject(new Error(`ssh.writeFile: err ${err}, remotePath: ${remotePath}`));\n }\n resolve(true);\n });\n });\n }\n\n public uploadFileUsingExistingSftp(sftp: SFTPWrapper, localPath: string, remotePath: string, mode: number = 0o660) {\n return new Promise((resolve, reject) => {\n readFile(localPath).then(async (result: Buffer) => {\n this.writeFile(sftp, remotePath, result, mode)\n .then(() => {\n resolve(undefined);\n })\n .catch((err) => {\n const msg = `uploadFileUsingExistingSftp: error ${err} occurred`;\n this.logger.error(msg);\n reject(new Error(msg));\n });\n });\n });\n }\n\n private async __uploadDirectory(sftp: SFTPWrapper, localDir: string, remoteDir: string, mode: number = 0o660): Promise<void> {\n return new Promise((resolve, reject) => {\n fs.readdir(localDir, async (err, files) => {\n if (err) {\n return reject(new Error(`ssh.__uploadDir: err ${err}, localDir: ${localDir}, remoteDir: ${remoteDir}`));\n }\n\n try {\n await this.__createRemoteDirectory(sftp, remoteDir);\n for (const file of files) {\n const localPath = upath.join(localDir, file);\n const remotePath = `${remoteDir}/${file}`;\n\n if (fs.lstatSync(localPath).isDirectory()) {\n await this.__uploadDirectory(sftp, localPath, remotePath, mode);\n } else {\n await this.uploadFileUsingExistingSftp(sftp, localPath, remotePath, mode);\n }\n }\n\n resolve();\n } catch (err) {\n const msg = `ssh.__uploadDir: catched err ${err}`;\n this.logger.error(msg);\n reject(new Error(msg));\n }\n });\n });\n }\n\n /**\n * Uploads a local directory and its contents (including subdirectories) to the remote server via SFTP.\n * @param localDir - The path to the local directory to upload.\n * @param remoteDir - The path to the remote directory on the server.\n * @returns A promise that resolves when the directory and its contents are uploaded.\n */\n public async uploadDirectory(localDir: string, remoteDir: string, mode: number = 0o660): Promise<void> {\n return new Promise((resolve, reject) => {\n this.withSftp(async (sftp: SFTPWrapper) => {\n try {\n await this.__uploadDirectory(sftp, localDir, remoteDir, mode);\n resolve();\n } catch (e: unknown) {\n reject(new Error(`ssh.uploadDirectory: ${e}`));\n }\n });\n });\n }\n\n /**\n * Ensures that a remote directory and all its parent directories exist.\n * @param sftp - The SFTP wrapper.\n * @param remotePath - The path to the remote directory.\n * @returns A promise that resolves when the directory is created.\n */\n private __createRemoteDirectory(sftp: SFTPWrapper, remotePath: string): Promise<void> {\n return new Promise((resolve, reject) => {\n const directories = remotePath.split('/');\n let currentPath = '';\n\n const createNext = (index: number) => {\n if (index >= directories.length) {\n return resolve();\n }\n\n currentPath += `${directories[index]}/`;\n\n sftp.stat(currentPath, (err) => {\n if (err) {\n sftp.mkdir(currentPath, (err) => {\n if (err) {\n return reject(new Error(`ssh.__createRemDir: err ${err}, remotePath: ${remotePath}`));\n }\n createNext(index + 1);\n });\n } else {\n createNext(index + 1);\n }\n });\n };\n\n createNext(0);\n });\n }\n\n /**\n * Ensures that a remote directory and all its parent directories exist.\n * @param sftp - The SFTP wrapper.\n * @param remotePath - The path to the remote directory.\n * @returns A promise that resolves when the directory is created.\n */\n public ensureRemoteDirCreated(remotePath: string, mode: number = 0o755): Promise<void> {\n return this.withSftp(async (sftp) => {\n const directories = remotePath.split('/');\n let currentPath = '';\n\n for (const directory of directories) {\n currentPath += `${directory}/`;\n\n try {\n await new Promise<void>((resolve, reject) => {\n sftp.stat(currentPath, (err) => {\n if (!err) return resolve();\n\n sftp.mkdir(currentPath, { mode }, (err) => {\n if (err) {\n return reject(new Error(`ssh.createRemoteDir: err ${err}, remotePath: ${remotePath}`));\n }\n resolve();\n });\n });\n });\n } catch (error) {\n console.error(`Failed to create directory: ${currentPath}`, error);\n throw error;\n }\n }\n });\n }\n\n /**\n * Downloads a file from the remote server to a local path via SFTP.\n * @param remotePath - The remote file path on the server.\n * @param localPath - The local file path to save the file.\n * @returns A promise resolving with `true` if the file was successfully downloaded.\n */\n public async downloadFile(remotePath: string, localPath: string): Promise<boolean> {\n return this.withSftp(async (sftp) => {\n return new Promise((resolve, reject) => {\n sftp.fastGet(remotePath, localPath, (err) => {\n if (err) {\n return reject(new Error(`ssh.downloadFile: err ${err}, remotePath: ${remotePath}, localPath: ${localPath}`));\n }\n resolve(true);\n });\n });\n });\n }\n\n /**\n * Closes the SSH client connection and forwarded ports.\n */\n public close(): void {\n this.closeForwardedPorts();\n this.client.end();\n }\n}\n\nexport type SshExecResult = { stdout: string; stderr: string };\n\nasync function connect(\n client: Client,\n config: ConnectConfig,\n onError: (e: unknown) => void,\n onClose: () => void,\n): Promise<Client> {\n return new Promise((resolve, reject) => {\n client.on('ready', () => {\n resolve(client);\n });\n\n client.on('error', (err: unknown) => {\n onError(err);\n reject(new Error(`ssh.connect: error occurred: ${err}`));\n });\n\n client.on('close', () => {\n onClose();\n });\n\n client.connect(config);\n });\n}\n\nasync function forwardOut(logger: MiLogger, conn: Client, localHost: string, localPort: number, remoteHost: string, remotePort: number): Promise<ClientChannel> {\n return new Promise((resolve, reject) => {\n conn.forwardOut(localHost, localPort, remoteHost, remotePort, (err, stream) => {\n if (err) {\n logger.error(`forwardOut.error: ${err}`);\n return reject(err);\n }\n\n return resolve(stream);\n });\n });\n}\n","/** Just a lot of hardcoded paths of our current ssh deployment. */\n\nimport upath from 'upath';\nimport { newArch } from '../common/os_and_arch';\nimport { getDefaultPlVersion } from '../common/pl_version';\n\nexport const minioDirName = 'minio-2024-12-18T13-15-44Z';\nexport const supervisordDirName = 'supervisord-0.7.3';\nexport const supervisordSubDirName = 'supervisord_0.7.3_Linux_64-bit';\n\nexport function workDir(remoteHome: string) {\n return upath.join(remoteHome, '.platforma_ssh');\n}\n\nexport function binariesDir(remoteHome: string) {\n return upath.join(workDir(remoteHome), 'binaries');\n}\n\nexport function platformaBaseDir(remoteHome: string, arch: string) {\n return upath.join(binariesDir(remoteHome), `pl-${getDefaultPlVersion()}-${newArch(arch)}`);\n}\n\nexport function platformaDir(remoteHome: string, arch: string) {\n return upath.join(platformaBaseDir(remoteHome, arch), 'binaries');\n}\n\nexport function platformaBin(remoteHome: string, arch: string): string {\n return upath.join(platformaDir(remoteHome, arch), 'platforma');\n}\n\nexport function platformaConf(remoteHome: string): string {\n return upath.join(workDir(remoteHome), 'config.yaml');\n}\n\nexport function platformaFreePortBin(remoteHome: string, arch: string): string {\n return upath.join(platformaDir(remoteHome, arch), 'free-port');\n}\n\nexport function minioDir(remoteHome: string, arch: string) {\n return upath.join(binariesDir(remoteHome), `minio-2024-12-18T13-15-44Z-${newArch(arch)}`);\n}\n\nexport function minioBin(remoteHome: string, arch: string) {\n return upath.join(minioDir(remoteHome, arch), 'minio');\n}\n\nexport function supervisorBinDir(remoteHome: string, arch: string) {\n return upath.join(binariesDir(remoteHome), `supervisord-0.7.3-${newArch(arch)}`, supervisordSubDirName);\n}\n\nexport function supervisorBin(remoteHome: string, arch: string): string {\n return upath.join(supervisorBinDir(remoteHome, arch), 'supervisord');\n}\n\nexport function supervisorConf(remoteHome: string) {\n return upath.join(workDir(remoteHome), 'supervisor.conf');\n}\n\nexport function connectionInfo(remoteHome: string) {\n return upath.join(workDir(remoteHome), `connection.txt`);\n}\n","/** Provides helper functions to work with supervisord */\n\nimport type { MiLogger } from '@milaboratories/ts-helpers';\nimport * as plpath from './pl_paths';\nimport type { SshClient, SshExecResult } from './ssh';\nimport { randomBytes } from 'crypto';\n\nexport async function supervisorCtlStart(\n sshClient: SshClient,\n remoteHome: string, arch: string,\n) {\n const result = await supervisorExec(sshClient, remoteHome, arch, '--daemon');\n\n if (result.stderr) {\n throw new Error(`Can not run ssh Platforma ${result.stderr}`);\n }\n}\n\nexport async function supervisorStop(\n sshClient: SshClient,\n remoteHome: string, arch: string,\n) {\n const result = await supervisorExec(sshClient, remoteHome, arch, 'ctl shutdown');\n\n if (result.stderr) {\n throw new Error(`Can not stop ssh Platforma ${result.stderr}`);\n }\n}\n\n/** Provides a simple true/false response got from supervisord status\n * along with a debug info that could be showed in error logs (raw response from the command, parsed response etc). */\nexport type SupervisorStatus = {\n platforma?: boolean;\n minio?: boolean;\n allAlive: boolean; // true when both pl and minio are alive.\n rawResult?: SshExecResult;\n execError?: string;\n};\n\nexport async function supervisorStatus(\n logger: MiLogger,\n sshClient: SshClient,\n remoteHome: string, arch: string,\n): Promise<SupervisorStatus> {\n let result: SshExecResult;\n try {\n result = await supervisorExec(sshClient, remoteHome, arch, 'ctl status');\n } catch (e: unknown) {\n return { execError: String(e), allAlive: false };\n }\n\n if (result.stderr) {\n logger.info(`supervisord ctl status: stderr occurred: ${result.stderr}, stdout: ${result.stdout}`);\n\n return { rawResult: result, allAlive: false };\n }\n\n const platforma = isProgramRunning(result.stdout, 'platforma');\n const minio = isProgramRunning(result.stdout, 'minio');\n const status: SupervisorStatus = {\n rawResult: result,\n platforma,\n minio,\n allAlive: platforma && minio,\n };\n\n if (status.allAlive) {\n return status;\n }\n\n if (!status.minio) {\n logger.warn('Minio is not running on the server');\n }\n\n if (!status.platforma) {\n logger.warn('Platforma is not running on the server');\n }\n\n return status;\n}\n\nexport function generateSupervisordConfig(\n minioStorageDir: string,\n minioEnvs: Record<string, string>,\n supervisorRemotePort: number,\n remoteWorkDir: string,\n platformaConfigPath: string,\n\n minioPath: string,\n plPath: string,\n) {\n const minioEnvStr = Object.entries(minioEnvs).map(([key, value]) => `${key}=\"${value}\"`).join(',');\n const password = randomBytes(16).toString('hex');\n const freePort = supervisorRemotePort;\n\n return `\n[supervisord]\nlogfile=${remoteWorkDir}/supervisord.log\nloglevel=info\npidfile=${remoteWorkDir}/supervisord.pid\n\n[inet_http_server]\nport=127.0.0.1:${freePort}\nusername=default-user\npassword=${password}\n\n[supervisorctl]\nserverurl=http://127.0.0.1:${freePort}\nusername=default-user\npassword=${password}\n\n[program:platforma]\nautostart=true\ndepends_on=minio\ncommand=${plPath} --config ${platformaConfigPath}\ndirectory=${remoteWorkDir}\nautorestart=true\n\n[program:minio]\nautostart=true\nenvironment=${minioEnvStr}\ncommand=${minioPath} server ${minioStorageDir}\ndirectory=${remoteWorkDir}\nautorestart=true\n`;\n}\n\nexport async function supervisorExec(\n sshClient: SshClient,\n remoteHome: string, arch: string,\n command: string,\n) {\n const supervisorCmd = plpath.supervisorBin(remoteHome, arch);\n const supervisorConf = plpath.supervisorConf(remoteHome);\n\n const cmd = `${supervisorCmd} --configuration ${supervisorConf} ${command}`;\n return await sshClient.exec(cmd);\n}\n\nfunction isProgramRunning(output: string, programName: string) {\n // eslint-disable-next-line no-control-regex\n const stripAnsi = (str: string) => str.replace(/\\x1B\\[[0-9;]*m/g, '');\n\n const cleanedOutput = stripAnsi(output);\n\n return cleanedOutput.split('\\n').some((line) => {\n const [name, status] = line.trim().split(/\\s{2,}/); // Split string by 2 spaces.\n\n return name === programName && status === 'Running';\n });\n}\n","/** We store all info about the connection on the server,\n * so that another client could read the file and connect from another machine. */\nimport { z } from 'zod';\n\n//\n// Types\n//\n\nexport const PortPair = z.object({\n local: z.number(),\n remote: z.number(),\n});\n/** The pair of ports for forwarding. */\nexport type PortPair = z.infer<typeof PortPair>;\n\nexport const SshPlPorts = z.object({\n grpc: PortPair,\n monitoring: PortPair,\n debug: PortPair,\n minioPort: PortPair,\n minioConsolePort: PortPair,\n});\n/** All info about ports that are forwarded. */\nexport type SshPlPorts = z.infer<typeof SshPlPorts>;\n\nexport const ConnectionInfo = z.object({\n plUser: z.string(),\n plPassword: z.string(),\n ports: SshPlPorts,\n\n // It's false by default because it was added later,\n // and in some deployments there won't be useGlobalAccess flag in the file.\n useGlobalAccess: z.boolean().default(false),\n\n // We added the field afterwards, the pl backend was this version.\n plVersion: z.string().default('1.18.3'),\n});\n/** The content of the file that holds all the info about the connection on the remote server. */\nexport type ConnectionInfo = z.infer<typeof ConnectionInfo>;\n\n//\n// Funcs\n//\n\nexport function newConnectionInfo(\n plUser: string,\n plPassword: string,\n ports: SshPlPorts,\n useGlobalAccess: boolean,\n plVersion: string,\n): ConnectionInfo {\n return {\n plUser,\n plPassword,\n ports,\n useGlobalAccess,\n plVersion,\n };\n}\n\nexport function parseConnectionInfo(content: string): ConnectionInfo {\n return ConnectionInfo.parse(JSON.parse(content));\n}\n\nexport function stringifyConnectionInfo(conn: ConnectionInfo): string {\n return JSON.stringify(conn, undefined, 2);\n}\n","import type * as ssh from 'ssh2';\nimport { SshClient } from './ssh';\nimport type { MiLogger } from '@milaboratories/ts-helpers';\nimport { sleep, notEmpty } from '@milaboratories/ts-helpers';\nimport type { DownloadBinaryResult } from '../common/pl_binary_download';\nimport { downloadBinaryNoExtract } from '../common/pl_binary_download';\nimport upath from 'upath';\nimport * as plpath from './pl_paths';\nimport { getDefaultPlVersion } from '../common/pl_version';\n\nimport net from 'net';\nimport type { PlLicenseMode, SshPlConfigGenerationResult } from '@milaboratories/pl-config';\nimport { generateSshPlConfigs, getFreePort } from '@milaboratories/pl-config';\nimport type { SupervisorStatus } from './supervisord';\nimport { supervisorStatus, supervisorStop as supervisorCtlShutdown, generateSupervisordConfig, supervisorCtlStart } from './supervisord';\nimport type { ConnectionInfo, SshPlPorts } from './connection_info';\nimport { newConnectionInfo, parseConnectionInfo, stringifyConnectionInfo } from './connection_info';\nimport type { PlBinarySourceDownload } from '../common/pl_binary';\n\nexport class SshPl {\n private initState: PlatformaInitState = {};\n constructor(\n public readonly logger: MiLogger,\n public readonly sshClient: SshClient,\n private readonly username: string,\n ) { }\n\n public info() {\n return {\n username: this.username,\n initState: this.initState,\n };\n }\n\n public static async init(logger: MiLogger, config: ssh.ConnectConfig): Promise<SshPl> {\n try {\n const sshClient = await SshClient.init(logger, config);\n return new SshPl(logger, sshClient, notEmpty(config.username));\n } catch (e: unknown) {\n logger.error(`Connection error in SshClient.init: ${e}`);\n throw e;\n }\n }\n\n public cleanUp() {\n this.sshClient.close();\n }\n\n /** Provides an info if the platforma and minio are running along with the debug info. */\n public async isAlive(): Promise<SupervisorStatus> {\n const arch = await this.getArch();\n const remoteHome = await this.getUserHomeDirectory();\n return await supervisorStatus(this.logger, this.sshClient, remoteHome, arch.arch);\n }\n\n /** Starts all the services on the server.\n * Idempotent semantic: we could call it several times. */\n public async start() {\n const arch = await this.getArch();\n const remoteHome = await this.getUserHomeDirectory();\n\n try {\n if (!(await this.isAlive()).allAlive) {\n await supervisorCtlStart(this.sshClient, remoteHome, arch.arch);\n\n // We are waiting for Platforma to run to ensure that it has started.\n return await this.checkIsAliveWithInterval();\n }\n } catch (e: unknown) {\n const msg = `SshPl.start: error occurred ${e}`;\n this.logger.error(msg);\n throw new Error(msg);\n }\n }\n\n /** Stops all the services on the server.\n * Idempotent semantic: we could call it several times. */\n public async stop() {\n const arch = await this.getArch();\n const remoteHome = await this.getUserHomeDirectory();\n\n try {\n if ((await this.isAlive()).allAlive) {\n await supervisorCtlShutdown(this.sshClient, remoteHome, arch.arch);\n return await this.checkIsAliveWithInterval(undefined, undefined, false);\n }\n } catch (e: unknown) {\n const msg = `PlSsh.stop: error occurred ${e}`;\n this.logger.error(msg);\n throw new Error(msg);\n }\n }\n\n /** Stops the services, deletes a directory with the state and closes SSH connection. */\n public async reset(): Promise<boolean> {\n await this.stopAndClean();\n this.cleanUp();\n return true;\n }\n\n /** Stops platforma and deletes its state. */\n public async stopAndClean(): Promise<void> {\n const remoteHome = await this.getUserHomeDirectory();\n\n this.logger.info(`pl.reset: Stop Platforma on the server`);\n await this.stop();\n\n this.logger.info(`pl.reset: Deleting Platforma workDir ${plpath.workDir(remoteHome)} on the server`);\n await this.sshClient.deleteFolder(plpath.workDir(remoteHome));\n }\n\n /** Downloads binaries and untar them on the server,\n * generates all the configs, creates necessary dirs,\n * and finally starts all the services. */\n public async platformaInit(options: SshPlConfig): Promise<ConnectionInfo> {\n const state: PlatformaInitState = { localWorkdir: options.localWorkdir };\n\n try {\n // merge options with default ops.\n const ops: SshPlConfig = {\n ...defaultSshPlConfig,\n ...options,\n };\n state.plBinaryOps = ops.plBinary;\n state.arch = await this.getArch();\n state.remoteHome = await this.getUserHomeDirectory();\n state.alive = await this.isAlive();\n\n if (state.alive.allAlive) {\n state.userCredentials = await this.getUserCredentials(state.remoteHome);\n if (!state.userCredentials) {\n throw new Error(`SshPl.platformaInit: platforma is alive but userCredentials are not found`);\n }\n const sameGA = state.userCredentials.useGlobalAccess == ops.useGlobalAccess;\n const samePlVersion = state.userCredentials.plVersion == ops.plBinary!.version;\n state.needRestart = !(sameGA && samePlVersion);\n this.logger.info(`SshPl.platformaInit: need restart? ${state.needRestart}`);\n\n if (!state.needRestart)\n return state.userCredentials;\n\n await this.stop();\n }\n\n const downloadRes = await this.downloadBinariesAndUploadToTheServer(\n ops.localWorkdir, ops.plBinary!, state.remoteHome, state.arch,\n );\n state.binPaths = { ...downloadRes, history: undefined };\n state.downloadedBinaries = downloadRes.history;\n\n state.ports = await this.fetchPorts(state.remoteHome, state.arch);\n\n if (!state.ports.debug.remote || !state.ports.grpc.remote || !state.ports.minioPort.remote || !state.ports.minioConsolePort.remote || !state.ports.monitoring.remote) {\n throw new Error(`SshPl.platformaInit: remote ports are not defined`);\n }\n\n const config = await generateSshPlConfigs({\n logger: this.logger,\n workingDir: plpath.workDir(state.remoteHome),\n portsMode: {\n type: 'customWithMinio',\n ports: {\n debug: state.ports.debug.remote,\n grpc: state.ports.grpc.remote,\n minio: state.ports.minioPort.remote,\n minioConsole: state.ports.minioConsolePort.remote,\n monitoring: state.ports.monitoring.remote,\n\n grpcLocal: state.ports.grpc.local,\n minioLocal: state.ports.minioPort.local,\n },\n },\n licenseMode: ops.license,\n useGlobalAccess: notEmpty(ops.useGlobalAccess),\n });\n state.generatedConfig = { ...config, filesToCreate: { skipped: 'it is too wordy' } };\n\n for (const [filePath, content] of Object.entries(config.filesToCreate)) {\n await this.sshClient.writeFileOnTheServer(filePath, content);\n this.logger.info(`Created file ${filePath}`);\n }\n\n for (const dir of config.dirsToCreate) {\n await this.sshClient.ensureRemoteDirCreated(dir);\n this.logger.info(`Created directory ${dir}`);\n }\n\n const supervisorConfig = generateSupervisordConfig(\n config.minioConfig.storageDir,\n config.minioConfig.envs,\n await this.getFreePortForPlatformaOnServer(state.remoteHome, state.arch),\n config.workingDir,\n config.plConfig.configPath,\n state.binPaths.minioRelPath,\n state.binPaths.downloadedPl,\n );\n\n const writeResult = await this.sshClient.writeFileOnTheServer(plpath.supervisorConf(state.remoteHome), supervisorConfig);\n if (!writeResult) {\n throw new Error(`Can not write supervisord config on the server ${plpath.workDir(state.remoteHome)}`);\n }\n\n state.connectionInfo = newConnectionInfo(\n config.plUser,\n config.plPassword,\n state.ports,\n notEmpty(ops.useGlobalAccess),\n ops.plBinary!.version,\n );\n await this.sshClient.writeFileOnTheServer(\n plpath.connectionInfo(state.remoteHome),\n stringifyConnectionInfo(state.connectionInfo),\n );\n\n await this.start();\n state.started = true;\n this.initState = state;\n\n return state.connectionInfo;\n } catch (e: unknown) {\n const msg = `SshPl.platformaInit: error occurred: ${e}, state: ${JSON.stringify(state)}`;\n this.logger.error(msg);\n\n throw new Error(msg);\n }\n }\n\n public async downloadBinariesAndUploadToTheServer(\n localWorkdir: string,\n plBinary: PlBinarySourceDownload,\n remoteHome: string,\n arch: Arch,\n ) {\n const state: DownloadAndUntarState[] = [];\n try {\n const pl = await this.downloadAndUntar(\n localWorkdir, remoteHome, arch,\n 'pl', `pl-${plBinary.version}`,\n );\n state.push(pl);\n\n const supervisor = await this.downloadAndUntar(\n localWorkdir, remoteHome, arch,\n 'supervisord', plpath.supervisordDirName,\n );\n state.push(supervisor);\n\n const minioPath = plpath.minioBin(remoteHome, arch.arch);\n const minio = await this.downloadAndUntar(\n localWorkdir, remoteHome, arch,\n 'minio', plpath.minioDirName,\n );\n state.push(minio);\n await this.sshClient.chmod(minioPath, 0o750);\n\n return {\n history: state,\n minioRelPath: minioPath,\n downloadedPl: plpath.platformaBin(remoteHome, arch.arch),\n };\n } catch (e: unknown) {\n const msg = `SshPl.downloadBinariesAndUploadToServer: error ${e} occurred, state: ${JSON.stringify(state)}`;\n this.logger.error(msg);\n throw e;\n }\n }\n\n /** We have to extract pl in the remote server,\n * because Windows doesn't support symlinks\n * that are found in Linux pl binaries tgz archive.\n * For this reason, we extract all to the remote server.\n * It requires `tar` to be installed on the server\n * (it's not installed for Rocky Linux for example). */\n public async downloadAndUntar(\n localWorkdir: string,\n remoteHome: string,\n arch: Arch,\n softwareName: string,\n tgzName: string,\n ): Promise<DownloadAndUntarState> {\n const state: DownloadAndUntarState = {};\n state.binBasePath = plpath.binariesDir(remoteHome);\n await this.sshClient.ensureRemoteDirCreated(state.binBasePath);\n state.binBasePathCreated = true;\n\n let downloadBinaryResult: DownloadBinaryResult | null = null;\n const attempts = 5;\n for (let i = 1; i <= attempts; i++) {\n try {\n downloadBinaryResult = await downloadBinaryNoExtract(\n this.logger,\n localWorkdir,\n softwareName,\n tgzName,\n arch.arch, arch.platform,\n );\n break;\n } catch (e: unknown) {\n await sleep(300);\n if (i == attempts) {\n throw new Error(`downloadAndUntar: ${attempts} attempts, last error: ${e}`);\n }\n }\n }\n state.downloadResult = notEmpty(downloadBinaryResult);\n\n state.localArchivePath = upath.resolve(state.downloadResult.archivePath);\n state.remoteDir = upath.join(state.binBasePath, state.downloadResult.baseName);\n state.remoteArchivePath = state.remoteDir + '.tgz';\n\n await this.sshClient.ensureRemoteDirCreated(state.remoteDir);\n await this.sshClient.uploadFile(state.localArchivePath, state.remoteArchivePath);\n state.uploadDone = true;\n\n // TODO: Create a proper archive to avoid xattr warnings\n const untarResult = await this.sshClient.exec(\n `tar --warning=no-all -xvf ${state.remoteArchivePath} --directory=${state.remoteDir}`,\n );\n\n if (untarResult.stderr)\n throw Error(`downloadAndUntar: untar: stderr occurred: ${untarResult.stderr}, stdout: ${untarResult.stdout}`);\n\n state.untarDone = true;\n\n return state;\n }\n\n public async needDownload(remoteHome: string, arch: Arch) {\n const checkPathSupervisor = plpath.supervisorBin(remoteHome, arch.arch);\n const checkPathMinio = plpath.minioDir(remoteHome, arch.arch);\n const checkPathPlatforma = plpath.platformaBin(remoteHome, arch.arch);\n\n if (!await this.sshClient.checkFileExists(checkPathPlatforma)\n || !await this.sshClient.checkFileExists(checkPathMinio)\n || !await this.sshClient.checkFileExists(checkPathSupervisor)) {\n return true;\n }\n\n return false;\n }\n\n public async checkIsAliveWithInterval(interval: number = 1000, count = 15, shouldStart = true): Promise<void> {\n const maxMs = count * interval;\n\n let total = 0;\n let alive = await this.isAlive();\n while (shouldStart ? !alive.allAlive : alive.allAlive) {\n await sleep(interval);\n total += interval;\n if (total > maxMs) {\n throw new Error(`isAliveWithInterval: The process did not ${shouldStart ? 'started' : 'stopped'} after ${maxMs} ms. Live status: ${JSON.stringify(alive)}`);\n }\n alive = await this.isAlive();\n }\n }\n\n public async getUserCredentials(remoteHome: string): Promise<ConnectionInfo> {\n const connectionInfo = await this.sshClient.readFile(plpath.connectionInfo(remoteHome));\n return parseConnectionInfo(connectionInfo);\n }\n\n public async fetchPorts(remoteHome: string, arch: Arch): Promise<SshPlPorts> {\n const ports: SshPlPorts = {\n grpc: {\n local: await getFreePort(),\n remote: await this.getFreePortForPlatformaOnServer(remoteHome, arch),\n },\n monitoring: {\n local: await getFreePort(),\n remote: await this.getFreePortForPlatformaOnServer(remoteHome, arch),\n },\n debug: {\n local: await getFreePort(),\n remote: await this.getFreePortForPlatformaOnServer(remoteHome, arch),\n },\n minioPort: {\n local: await getFreePort(),\n remote: await this.getFreePortForPlatformaOnServer(remoteHome, arch),\n },\n minioConsolePort: {\n local: await getFreePort(),\n remote: await this.getFreePortForPlatformaOnServer(remoteHome, arch),\n },\n };\n\n return ports;\n }\n\n public async getLocalFreePort(): Promise<number> {\n return new Promise((res) => {\n const srv = net.createServer();\n srv.listen(0, () => {\n const port = (srv.address() as net.AddressInfo).port;\n srv.close((_) => res(port));\n });\n });\n }\n\n public async getFreePortForPlatformaOnServer(remoteHome: string, arch: Arch): Promise<number> {\n const freePortBin = plpath.platformaFreePortBin(remoteHome, arch.arch);\n\n const { stdout, stderr } = await this.sshClient.exec(`${freePortBin}`);\n if (stderr) {\n throw new Error(`getFreePortForPlatformaOnServer: stderr is not empty: ${stderr}, stdout: ${stdout}`);\n }\n\n return +stdout;\n }\n\n public async getArch(): Promise<Arch> {\n const { stdout, stderr } = await this.sshClient.exec('uname -s && uname -m');\n if (stderr)\n throw new Error(`getArch: stderr is not empty: ${stderr}, stdout: ${stdout}`);\n\n const arr = stdout.split('\\n');\n\n return {\n platform: arr[0],\n arch: arr[1],\n };\n }\n\n public async getUserHomeDirectory() {\n const { stdout, stderr } = await this.sshClient.exec('echo $HOME');\n\n if (stderr) {\n const home = `/home/${this.username}`;\n console.warn(`getUserHomeDirectory: stderr is not empty: ${stderr}, stdout: ${stdout}, will get a default home: ${home}`);\n\n return home;\n }\n\n return stdout.trim();\n }\n}\n\ntype Arch = { platform: string; arch: string };\n\nexport type SshPlConfig = {\n localWorkdir: string;\n license: PlLicenseMode;\n useGlobalAccess?: boolean;\n plBinary?: PlBinarySourceDownload;\n};\n\nconst defaultSshPlConfig: Pick<\n SshPlConfig,\n | 'useGlobalAccess'\n | 'plBinary'\n> = {\n useGlobalAccess: false,\n plBinary: {\n type: 'Download',\n version: getDefaultPlVersion(),\n },\n};\n\ntype BinPaths = {\n history?: DownloadAndUntarState[];\n minioRelPath: string;\n downloadedPl: string;\n};\n\ntype DownloadAndUntarState = {\n binBasePath?: string;\n binBasePathCreated?: boolean;\n downloadResult?: DownloadBinaryResult;\n attempts?: number;\n\n localArchivePath?: string;\n remoteDir?: string;\n remoteArchivePath?: string;\n uploadDone?: boolean;\n untarDone?: boolean;\n};\n\ntype PlatformaInitState = {\n localWorkdir?: string;\n plBinaryOps?: PlBinarySourceDownload;\n arch?: Arch;\n remoteHome?: string;\n alive?: SupervisorStatus;\n userCredentials?: ConnectionInfo;\n needRestart?: boolean;\n downloadedBinaries?: DownloadAndUntarState[];\n binPaths?: BinPaths;\n ports?: SshPlPorts;\n generatedConfig?: SshPlConfigGenerationResult;\n connectionInfo?: ConnectionInfo;\n started?: boolean;\n};\n"],"names":["processRun","logger","opts","spawn","isProcessAlive","pid","processStop","processWaitStopped","maxMs","total","sleep","OSes","newOs","osName","Arches","newArch","arch","cdn","gaCdn","downloadBinaryNoExtract","baseDir","softwareName","tgzName","platform","getPathsForDownload","archiveUrl","alternativeArchiveGAUrl","archivePath","downloadArchive","downloadBinary","archiveName","archiveType","targetFolder","baseName","extractArchive","os","osToArchiveType","archiveFileName","upath","dstArchiveFile","state","fileExists","fsp","body","statusCode","request","textBody","text","Readable","Writable","fs","e","msg","MarkerFileName","dstFolder","markerFilePath","tar","decompress","assertNever","getDefaultPlVersion","newDefaultPlBinarySource","resolveLocalPlBinaryPath","downloadDir","src","ops","osToBinaryName","filePid","dir","readPid","filePath","writePid","newTrace","trace","t","k","v","withTrace","fn","LocalConfigYaml","LocalPl","workingDir","startOptions","initialStartHistory","onClose","onError","onCloseAndError","onCloseAndErrorNoStop","__publicField","instance","pidFile","notEmpty","localPlatformaInit","_ops","workDir","localPlatformaReadPidAndStop","configPath","plBinPath","baseBinaryPath","processOpts","pl","file","oldPid","alive","defaultConfig","SshClient","client","config","withDefaults","Client","_a","_b","connect","command","resolve","reject","err","stream","stdout","stderr","code","data","host","port","conn","types","log","match","method","ports","randomBytes","persistentClient","RetryablePromise","p","server","net","localSocket","sockLog","forwardOut","s","rawAddress","address","hostname","dns","privateKey","ssh","localPath","remotePath","sftp","newErr","callback","mode","items","item","itemPath","error","path","list","a","b","directoryPath","message","buffer","stats","readFile","result","localDir","remoteDir","files","directories","currentPath","createNext","index","directory","localHost","localPort","remoteHost","remotePort","minioDirName","supervisordDirName","supervisordSubDirName","remoteHome","binariesDir","platformaBaseDir","platformaDir","platformaBin","platformaFreePortBin","minioDir","minioBin","supervisorBinDir","supervisorBin","supervisorConf","connectionInfo","supervisorCtlStart","sshClient","supervisorExec","supervisorStop","supervisorStatus","platforma","isProgramRunning","minio","status","generateSupervisordConfig","minioStorageDir","minioEnvs","supervisorRemotePort","remoteWorkDir","platformaConfigPath","minioPath","plPath","minioEnvStr","key","value","password","freePort","supervisorCmd","plpath.supervisorBin","plpath.supervisorConf","cmd","output","programName","str","line","name","PortPair","z","SshPlPorts","ConnectionInfo","newConnectionInfo","plUser","plPassword","useGlobalAccess","plVersion","parseConnectionInfo","content","stringifyConnectionInfo","SshPl","username","supervisorCtlShutdown","plpath.workDir","options","defaultSshPlConfig","sameGA","samePlVersion","downloadRes","generateSshPlConfigs","supervisorConfig","plpath.connectionInfo","localWorkdir","plBinary","supervisor","plpath.supervisordDirName","plpath.minioBin","plpath.minioDirName","plpath.platformaBin","plpath.binariesDir","downloadBinaryResult","attempts","i","untarResult","checkPathSupervisor","checkPathMinio","plpath.minioDir","checkPathPlatforma","interval","count","shouldStart","getFreePort","res","srv","_","freePortBin","plpath.platformaFreePortBin","arr","home"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@milaboratories/pl-deployments",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"pl-version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
|
+
"pl-version": "1.23.0",
|
|
5
5
|
"description": "MiLaboratories Platforma Backend code service run wrapper",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
7
7
|
"main": "./dist/index.js",
|
|
@@ -50,8 +50,8 @@
|
|
|
50
50
|
"undici": "~7.2.3",
|
|
51
51
|
"yaml": "^2.6.1",
|
|
52
52
|
"zod": "~3.23.8",
|
|
53
|
-
"@milaboratories/
|
|
54
|
-
"@milaboratories/
|
|
53
|
+
"@milaboratories/pl-config": "^1.4.3",
|
|
54
|
+
"@milaboratories/ts-helpers": "^1.1.4"
|
|
55
55
|
},
|
|
56
56
|
"scripts": {
|
|
57
57
|
"type-check": "tsc --noEmit --composite false",
|
package/src/local/pl.ts
CHANGED
|
@@ -169,7 +169,7 @@ export async function localPlatformaInit(logger: MiLogger, _ops: LocalPlOptions)
|
|
|
169
169
|
|
|
170
170
|
const processOpts: ProcessOptions = {
|
|
171
171
|
cmd: binaryPath,
|
|
172
|
-
args: ['
|
|
172
|
+
args: ['--config', configPath],
|
|
173
173
|
opts: {
|
|
174
174
|
env: { ...process.env },
|
|
175
175
|
cwd: workDir,
|