@milaboratories/pl-deployments 1.2.0 → 1.2.2
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 +5 -5
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +32 -31
- package/dist/index.mjs.map +1 -1
- package/dist/ssh/ssh.d.ts.map +1 -1
- package/package.json +6 -6
- package/src/ssh/ssh.ts +15 -0
package/dist/index.js
CHANGED
|
@@ -4,8 +4,8 @@ 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.
|
|
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
|
|
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.24.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
|
+
`}})})}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}u.setNoDelay(!0),a.setNoDelay(!0);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),s.setNoDelay(!0)})}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 Nt="minio-2024-12-18T13-15-44Z",Bt="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 N(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 N(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 N(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
|
|
11
11
|
loglevel=info
|
|
@@ -34,7 +34,7 @@ environment=${c}
|
|
|
34
34
|
command=${o} server ${s}
|
|
35
35
|
directory=${e}
|
|
36
36
|
autorestart=true
|
|
37
|
-
`}async function
|
|
38
|
-
`).some(i=>{const[o,n]=i.trim().split(/\s{2,}/);return o===r&&n==="Running"})}const P=p.z.object({local:p.z.number(),remote:p.z.number()}),Y=p.z.object({grpc:P,monitoring:P,debug:P,minioPort:P,minioConsolePort:P}),Z=p.z.object({plUser:p.z.string(),plPassword:p.z.string(),ports:Y,useGlobalAccess:p.z.boolean().default(!1),plVersion:p.z.string().default("1.18.3")});function Q(s,r,t,e,i){return{plUser:s,plPassword:r,ports:t,useGlobalAccess:e,plVersion:i}}function X(s){return Z.parse(JSON.parse(s))}function tt(s){return JSON.stringify(s,void 0,2)}class
|
|
39
|
-
`);return{platform:e[0],arch:e[1]}}async getUserHomeDirectory(){const{stdout:r,stderr:t}=await this.sshClient.exec("echo $HOME");if(t){const e=`/home/${this.username}`;return console.warn(`getUserHomeDirectory: stderr is not empty: ${t}, stdout: ${r}, will get a default home: ${e}`),e}return r.trim()}}const Jt={useGlobalAccess:!1,plBinary:{type:"Download",version:E()}};exports.ConnectionInfo=Z;exports.LocalConfigYaml=J;exports.LocalPl=L;exports.PortPair=P;exports.SshClient=A;exports.SshPl=
|
|
37
|
+
`}async function N(s,r,t,e){const i=V(r,t),o=K(r),n=`${i} --configuration ${o} ${e}`;return await s.exec(n)}function T(s,r){return(i=>i.replace(/\x1B\[[0-9;]*m/g,""))(s).split(`
|
|
38
|
+
`).some(i=>{const[o,n]=i.trim().split(/\s{2,}/);return o===r&&n==="Running"})}const P=p.z.object({local:p.z.number(),remote:p.z.number()}),Y=p.z.object({grpc:P,monitoring:P,debug:P,minioPort:P,minioConsolePort:P}),Z=p.z.object({plUser:p.z.string(),plPassword:p.z.string(),ports:Y,useGlobalAccess:p.z.boolean().default(!1),plVersion:p.z.string().default("1.18.3")});function Q(s,r,t,e,i){return{plUser:s,plPassword:r,ports:t,useGlobalAccess:e,plVersion:i}}function X(s){return Z.parse(JSON.parse(s))}function tt(s){return JSON.stringify(s,void 0,2)}class B{constructor(r,t,e){f(this,"initState",{});this.logger=r,this.sshClient=t,this.username=e}info(){return{username:this.username,initState:this.initState}}static async init(r,t){try{const e=await A.init(r,t);return new B(r,e,w.notEmpty(t.username))}catch(e){throw r.error(`Connection error in SshClient.init: ${e}`),e}}cleanUp(){this.sshClient.close()}async isAlive(){const r=await this.getArch(),t=await this.getUserHomeDirectory();return await zt(this.logger,this.sshClient,t,r.arch)}async start(){const r=await this.getArch(),t=await this.getUserHomeDirectory();try{if(!(await this.isAlive()).allAlive)return await Ht(this.sshClient,t,r.arch),await this.checkIsAliveWithInterval()}catch(e){const i=`SshPl.start: error occurred ${e}`;throw this.logger.error(i),new Error(i)}}async stop(){const r=await this.getArch(),t=await this.getUserHomeDirectory();try{if((await this.isAlive()).allAlive)return await qt(this.sshClient,t,r.arch),await this.checkIsAliveWithInterval(void 0,void 0,!1)}catch(e){const i=`PlSsh.stop: error occurred ${e}`;throw this.logger.error(i),new Error(i)}}async reset(){return await this.stopAndClean(),this.cleanUp(),!0}async stopAndClean(){const r=await this.getUserHomeDirectory();this.logger.info("pl.reset: Stop Platforma on the server"),await this.stop(),this.logger.info(`pl.reset: Deleting Platforma workDir ${g(r)} on the server`),await this.sshClient.deleteFolder(g(r))}async platformaInit(r){const t={localWorkdir:r.localWorkdir};try{const e={...Jt,...r};if(t.plBinaryOps=e.plBinary,t.arch=await this.getArch(),t.remoteHome=await this.getUserHomeDirectory(),t.alive=await this.isAlive(),t.alive.allAlive){if(t.userCredentials=await this.getUserCredentials(t.remoteHome),!t.userCredentials)throw new Error("SshPl.platformaInit: platforma is alive but userCredentials are not found");const a=t.userCredentials.useGlobalAccess==e.useGlobalAccess,l=t.userCredentials.plVersion==e.plBinary.version;if(t.needRestart=!(a&&l),this.logger.info(`SshPl.platformaInit: need restart? ${t.needRestart}`),!t.needRestart)return t.userCredentials;await this.stop()}const i=await this.downloadBinariesAndUploadToTheServer(e.localWorkdir,e.plBinary,t.remoteHome,t.arch);if(t.binPaths={...i,history:void 0},t.downloadedBinaries=i.history,t.ports=await this.fetchPorts(t.remoteHome,t.arch),!t.ports.debug.remote||!t.ports.grpc.remote||!t.ports.minioPort.remote||!t.ports.minioConsolePort.remote||!t.ports.monitoring.remote)throw new Error("SshPl.platformaInit: remote ports are not defined");const o=await $.generateSshPlConfigs({logger:this.logger,workingDir:g(t.remoteHome),portsMode:{type:"customWithMinio",ports:{debug:t.ports.debug.remote,grpc:t.ports.grpc.remote,minio:t.ports.minioPort.remote,minioConsole:t.ports.minioConsolePort.remote,monitoring:t.ports.monitoring.remote,grpcLocal:t.ports.grpc.local,minioLocal:t.ports.minioPort.local}},licenseMode:e.license,useGlobalAccess:w.notEmpty(e.useGlobalAccess)});t.generatedConfig={...o,filesToCreate:{skipped:"it is too wordy"}};for(const[a,l]of Object.entries(o.filesToCreate))await this.sshClient.writeFileOnTheServer(a,l),this.logger.info(`Created file ${a}`);for(const a of o.dirsToCreate)await this.sshClient.ensureRemoteDirCreated(a),this.logger.info(`Created directory ${a}`);const n=Mt(o.minioConfig.storageDir,o.minioConfig.envs,await this.getFreePortForPlatformaOnServer(t.remoteHome,t.arch),o.workingDir,o.plConfig.configPath,t.binPaths.minioRelPath,t.binPaths.downloadedPl);if(!await this.sshClient.writeFileOnTheServer(K(t.remoteHome),n))throw new Error(`Can not write supervisord config on the server ${g(t.remoteHome)}`);return t.connectionInfo=Q(o.plUser,o.plPassword,t.ports,w.notEmpty(e.useGlobalAccess),e.plBinary.version),await this.sshClient.writeFileOnTheServer(_(t.remoteHome),tt(t.connectionInfo)),await this.start(),t.started=!0,this.initState=t,t.connectionInfo}catch(e){const i=`SshPl.platformaInit: error occurred: ${e}, state: ${JSON.stringify(t)}`;throw this.logger.error(i),new Error(i)}}async downloadBinariesAndUploadToTheServer(r,t,e,i){const o=[];try{const n=await this.downloadAndUntar(r,e,i,"pl",`pl-${t.version}`);o.push(n);const c=await this.downloadAndUntar(r,e,i,"supervisord",Bt);o.push(c);const a=Tt(e,i.arch),l=await this.downloadAndUntar(r,e,i,"minio",Nt);return o.push(l),await this.sshClient.chmod(a,488),{history:o,minioRelPath:a,downloadedPl:I(e,i.arch)}}catch(n){const c=`SshPl.downloadBinariesAndUploadToServer: error ${n} occurred, state: ${JSON.stringify(o)}`;throw this.logger.error(c),n}}async downloadAndUntar(r,t,e,i,o){const n={};n.binBasePath=b(t),await this.sshClient.ensureRemoteDirCreated(n.binBasePath),n.binBasePathCreated=!0;let c=null;const a=5;for(let u=1;u<=a;u++)try{c=await mt(this.logger,r,i,o,e.arch,e.platform);break}catch(d){if(await w.sleep(300),u==a)throw new Error(`downloadAndUntar: ${a} attempts, last error: ${d}`)}n.downloadResult=w.notEmpty(c),n.localArchivePath=h.resolve(n.downloadResult.archivePath),n.remoteDir=h.join(n.binBasePath,n.downloadResult.baseName),n.remoteArchivePath=n.remoteDir+".tgz",await this.sshClient.ensureRemoteDirCreated(n.remoteDir),await this.sshClient.uploadFile(n.localArchivePath,n.remoteArchivePath),n.uploadDone=!0;const l=await this.sshClient.exec(`tar --warning=no-all -xvf ${n.remoteArchivePath} --directory=${n.remoteDir}`);if(l.stderr)throw Error(`downloadAndUntar: untar: stderr occurred: ${l.stderr}, stdout: ${l.stdout}`);return n.untarDone=!0,n}async needDownload(r,t){const e=V(r,t.arch),i=W(r,t.arch),o=I(r,t.arch);return!await this.sshClient.checkFileExists(o)||!await this.sshClient.checkFileExists(i)||!await this.sshClient.checkFileExists(e)}async checkIsAliveWithInterval(r=1e3,t=15,e=!0){const i=t*r;let o=0,n=await this.isAlive();for(;e?!n.allAlive:n.allAlive;){if(await w.sleep(r),o+=r,o>i)throw new Error(`isAliveWithInterval: The process did not ${e?"started":"stopped"} after ${i} ms. Live status: ${JSON.stringify(n)}`);n=await this.isAlive()}}async getUserCredentials(r){const t=await this.sshClient.readFile(_(r));return X(t)}async fetchPorts(r,t){return{grpc:{local:await $.getFreePort(),remote:await this.getFreePortForPlatformaOnServer(r,t)},monitoring:{local:await $.getFreePort(),remote:await this.getFreePortForPlatformaOnServer(r,t)},debug:{local:await $.getFreePort(),remote:await this.getFreePortForPlatformaOnServer(r,t)},minioPort:{local:await $.getFreePort(),remote:await this.getFreePortForPlatformaOnServer(r,t)},minioConsolePort:{local:await $.getFreePort(),remote:await this.getFreePortForPlatformaOnServer(r,t)}}}async getLocalFreePort(){return new Promise(r=>{const t=j.createServer();t.listen(0,()=>{const e=t.address().port;t.close(i=>r(e))})})}async getFreePortForPlatformaOnServer(r,t){const e=_t(r,t.arch),{stdout:i,stderr:o}=await this.sshClient.exec(`${e}`);if(o)throw new Error(`getFreePortForPlatformaOnServer: stderr is not empty: ${o}, stdout: ${i}`);return+i}async getArch(){const{stdout:r,stderr:t}=await this.sshClient.exec("uname -s && uname -m");if(t)throw new Error(`getArch: stderr is not empty: ${t}, stdout: ${r}`);const e=r.split(`
|
|
39
|
+
`);return{platform:e[0],arch:e[1]}}async getUserHomeDirectory(){const{stdout:r,stderr:t}=await this.sshClient.exec("echo $HOME");if(t){const e=`/home/${this.username}`;return console.warn(`getUserHomeDirectory: stderr is not empty: ${t}, stdout: ${r}, will get a default home: ${e}`),e}return r.trim()}}const Jt={useGlobalAccess:!1,plBinary:{type:"Download",version:E()}};exports.ConnectionInfo=Z;exports.LocalConfigYaml=J;exports.LocalPl=L;exports.PortPair=P;exports.SshClient=A;exports.SshPl=B;exports.SshPlPorts=Y;exports.getDefaultPlVersion=E;exports.localPlatformaInit=Dt;exports.newConnectionInfo=Q;exports.parseConnectionInfo=X;exports.stringifyConnectionInfo=tt;
|
|
40
40
|
//# sourceMappingURL=index.js.map
|