@milaboratories/pl-deployments 2.4.4 → 2.4.6
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/README.md +24 -1
- package/dist/index.js +13 -9
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +124 -112
- package/dist/index.mjs.map +1 -1
- package/dist/ssh/pl.d.ts.map +1 -1
- package/dist/ssh/pl_paths.d.ts +1 -0
- package/dist/ssh/pl_paths.d.ts.map +1 -1
- package/dist/ssh/supervisord.d.ts +5 -0
- package/dist/ssh/supervisord.d.ts.map +1 -1
- package/package.json +7 -6
- package/src/ssh/pl.ts +10 -1
- package/src/ssh/pl_paths.ts +4 -0
- package/src/ssh/supervisord.ts +9 -0
package/README.md
CHANGED
|
@@ -15,4 +15,27 @@ require("fs").writeFileSync('test-assets/private-key.pem', keys.private);
|
|
|
15
15
|
```
|
|
16
16
|
|
|
17
17
|
### Clean docker containers
|
|
18
|
-
If something went wrong with testcontainers, use `pnpm run cleanup-docker` from pl-deployments root.
|
|
18
|
+
If something went wrong with testcontainers, use `pnpm run cleanup-docker` from pl-deployments root.
|
|
19
|
+
|
|
20
|
+
### Dev container
|
|
21
|
+
The command starts a docker container for dev:
|
|
22
|
+
```sh
|
|
23
|
+
pnpm run dev-docker
|
|
24
|
+
# in another terminal you can start bash
|
|
25
|
+
pnpm run exec-dev-docker
|
|
26
|
+
```
|
|
27
|
+
Credentials:
|
|
28
|
+
127.0.0.1:4343
|
|
29
|
+
pl-doctor
|
|
30
|
+
password
|
|
31
|
+
|
|
32
|
+
### supervisor commands
|
|
33
|
+
Go to SSH server, then to `~/.platforma_ssh` and check the status of binary:
|
|
34
|
+
```sh
|
|
35
|
+
./binaries/supervisord-0.7.3-amd64/supervisord_0.7.3_Linux_64-bit/supervisord -c supervisor.conf ctl status
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
To shutdown platforma:
|
|
39
|
+
```sh
|
|
40
|
+
./binaries/supervisord-0.7.3-amd64/supervisord_0.7.3_Linux_64-bit/supervisord -c supervisor.conf ctl shutdown
|
|
41
|
+
```
|
package/dist/index.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
"use strict";var at=Object.defineProperty;var ct=(n,t,e)=>t in n?at(n,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):n[t]=e;var
|
|
1
|
+
"use strict";var at=Object.defineProperty;var ct=(n,t,e)=>t in n?at(n,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):n[t]=e;var f=(n,t,e)=>ct(n,typeof t!="symbol"?t+"":t,e);Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const lt=require("node:child_process"),p=require("@milaboratories/ts-helpers"),D=require("node:fs"),y=require("node:fs/promises"),u=require("upath"),dt=require("undici"),I=require("node:stream"),ut=require("node:stream/consumers"),ht=require("tar"),pt=require("decompress"),P=require("node:os"),C=require("ssh2"),H=require("node:net"),ft=require("node:dns"),b=require("node:crypto"),v=require("@milaboratories/pl-config"),w=require("zod");function q(n){const t=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(n){for(const e in n)if(e!=="default"){const r=Object.getOwnPropertyDescriptor(n,e);Object.defineProperty(t,e,r.get?r:{enumerable:!0,get:()=>n[e]})}}return t.default=n,Object.freeze(t)}const wt=q(ht),mt=q(P);function yt(n,t){return n.info(`Running:
|
|
2
2
|
cmd: ${JSON.stringify([t.cmd,...t.args])}
|
|
3
|
-
wd: ${t.opts.cwd}`),n.info(" spawning child process"),lt.spawn(t.cmd,t.args,t.opts)}async function k(n){try{return process.kill(n,0),!0}catch{return!1}}function G(n){return process.kill(n,"SIGINT")}async function z(n,t){let r=0;for(;await k(n);)if(await p.sleep(100),r+=100,r>t)throw new Error(`The process did not stopped after ${t} ms.`)}const gt=["linux","macos","windows"];function
|
|
3
|
+
wd: ${t.opts.cwd}`),n.info(" spawning child process"),lt.spawn(t.cmd,t.args,t.opts)}async function k(n){try{return process.kill(n,0),!0}catch{return!1}}function G(n){return process.kill(n,"SIGINT")}async function z(n,t){let r=0;for(;await k(n);)if(await p.sleep(100),r+=100,r>t)throw new Error(`The process did not stopped after ${t} ms.`)}const gt=["linux","macos","windows"];function _(n){switch(n.toLowerCase()){case"darwin":return"macos";case"linux":return"linux";case"win32":return"windows";default:throw new Error(`operating system '${n}' is not currently supported by Platforma ecosystem. The list of OSes supported: `+JSON.stringify(gt))}}const vt=["amd64","arm64"];function S(n){switch(n){case"aarch64":case"aarch64_be":case"arm64":return"arm64";case"x86_64":case"x64":return"amd64";default:throw new Error(`processor architecture '${n}' is not currently supported by Platforma ecosystem. The list of architectures supported: `+JSON.stringify(vt))}}const $t="https://cdn.platforma.bio/software",St="https://cdn-ga.pl-open.science/software";async function Ct(n,t,e,r,i,o){const s=L(e,r,t,S(i),_(o)),{archiveUrl:a,alternativeArchiveGAUrl:c,archivePath:l}=s;try{await F(n,a,l),s.wasDownloadedFrom=a}catch{await F(n,c,l),s.wasDownloadedFrom=c}return s}async function Et(n,t,e,r,i,o){const s=L(e,r,t,S(i),_(o)),{archiveUrl:a,alternativeArchiveGAUrl:c,archivePath:l,archiveType:d,targetFolder:h}=s;try{await F(n,a,l),s.wasDownloadedFrom=a}catch{await F(n,c,l),s.wasDownloadedFrom=c}return await bt(n,l,d,h),s}function L(n,t,e,r,i){const o=`${t}-${r}`,s=Ft[i],a=`${o}.${s}`,c=`${$t}/${n}/${i}/${a}`,l=`${St}/${n}/${i}/${a}`,d=u.join(e,a),h=u.join(e,o);return{archiveUrl:c,alternativeArchiveGAUrl:l,archivePath:d,archiveType:s,targetFolder:h,baseName:o}}async function F(n,t,e){const r={};r.dstArchive=e;try{if(r.fileExisted=await p.fileExists(e),r.fileExisted)return n.info(`Platforma Backend archive download skipped: '${e}' already exists`),r;await y.mkdir(u.dirname(e),{recursive:!0}),r.dirnameCreated=!0,n.info(`Downloading archive:
|
|
4
4
|
URL: ${t}
|
|
5
|
-
Save to: ${e}`);const{body:i,statusCode:o}=await dt.request(t);if(r.statusCode=o,o!=200){const s=await ut.text(i);throw r.errorMsg=`failed to download archive: ${o}, response: ${s.slice(0,1e3)}`,n.error(r.errorMsg),new Error(r.errorMsg)}return r.tmpPath=e+".tmp",await
|
|
5
|
+
Save to: ${e}`);const{body:i,statusCode:o}=await dt.request(t);if(r.statusCode=o,o!=200){const s=await ut.text(i);throw r.errorMsg=`failed to download archive: ${o}, response: ${s.slice(0,1e3)}`,n.error(r.errorMsg),new Error(r.errorMsg)}return r.tmpPath=e+".tmp",await I.Readable.toWeb(i).pipeTo(I.Writable.toWeb(D.createWriteStream(r.tmpPath))),r.wroteTmp=!0,r.tmpExisted=await p.fileExists(r.tmpPath),await y.rename(r.tmpPath,e),r.renamed=!0,r.newExisted=await p.fileExists(e),r}catch(i){const o=`downloadArchive: ${JSON.stringify(i)}, state: ${JSON.stringify(r)}`;throw n.error(o),new Error(o)}}const Pt=".ok";async function bt(n,t,e,r){if(n.info("extracting archive..."),n.info(` archive path: '${t}'`),n.info(` target dir: '${r}'`),!await p.fileExists(t)){const o=`Platforma Backend binary archive not found at '${t}'`;throw n.error(o),new Error(o)}const i=u.join(r,Pt);if(await p.fileExists(i)){n.info(`Platforma Backend binaries unpack skipped: '${r}' exists`);return}switch(await p.fileExists(r)&&(n.info(`Removing previous incompletely unpacked folder: '${r}'`),await y.rm(r,{recursive:!0})),n.info(` creating target dir '${r}'`),await y.mkdir(r,{recursive:!0}),n.info(`Unpacking Platforma Backend archive:
|
|
6
6
|
Archive: ${t}
|
|
7
|
-
Target dir: ${r}`),e){case"tgz":await
|
|
8
|
-
`}})})}static extractAuthMethods(t){const e=t.match(/Inbound: Received USERAUTH_FAILURE \((.+)\)/);return e&&e[1]?e[1].split(",").map(r=>r.trim()):[]}async forwardPort(t,e){const r=`ssh.forward:${t.localPort}:${t.remotePort}.id_${b.randomBytes(1).toString("hex")}`;e=e??this.config;const i=new p.RetryablePromise(o=>new Promise((s,a)=>{const c=new C.Client;c.on("ready",()=>{this.logger.info(`${r}.client.ready`),s(c)}),c.on("error",l=>{this.logger.info(`${r}.client.error: ${l}`),o.reset(),a(l)}),c.on("close",()=>{this.logger.info(`${r}.client.closed`),o.reset()}),c.connect(e)}));return await i.ensure(),new Promise((o,s)=>{const a=H.createServer({pauseOnConnect:!0},async c=>{const l=`${r}.sock_${b.randomBytes(1).toString("hex")}`;let d;try{d=await i.ensure()}catch(m){this.logger.info(`${l}.persistentClient.catch: ${m}`),c.end();return}d.setNoDelay(!0),c.setNoDelay(!0);let h;try{h=await Tt(this.logger,d,"127.0.0.1",0,"127.0.0.1",t.remotePort)}catch(m){this.logger.error(`${l}.forwardOut.err: ${m}`),c.end();return}c.pipe(h),h.pipe(c),c.resume(),h.on("error",m=>{this.logger.error(`${l}.stream.error: ${m}`),c.end(),h.end()}),h.on("close",()=>{c.end(),h.end()}),c.on("close",()=>{this.logger.info(`${l}.localSocket: closed`),c.end(),h.end()})});a.listen(t.localPort,"127.0.0.1",()=>{this.logger.info(`${r}.server: started listening`),this.forwardedServers.push(a),o({server:a})}),a.on("error",c=>{a.close(),s(new Error(`${r}.server: error: ${JSON.stringify(c)}`))}),a.on("close",()=>{this.logger.info(`${r}.server: closed ${JSON.stringify(t)}`),this.forwardedServers=this.forwardedServers.filter(c=>c!==a)})})}closeForwardedPorts(){this.logger.info("[SSH] Closing all forwarded ports..."),this.forwardedServers.forEach(t=>{const e=t.address();if(e&&typeof e!="string"){const r=e;this.logger.info(`[SSH] Closing port forward for server ${r.address}:${r.port}`)}t.close()}),this.forwardedServers=[]}static async checkHostAvailability(t){return new Promise(e=>{
|
|
7
|
+
Target dir: ${r}`),e){case"tgz":await wt.x({file:t,cwd:r,gzip:!0});break;case"zip":await pt(t,r);break;default:p.assertNever(e)}await y.writeFile(i,"ok"),n.info(" ... unpack done.")}const Ft={linux:"tgz",macos:"tgz",windows:"zip"};function A(){return"1.35.2"}function At(){return{type:"Download",version:A()}}async function Ot(n,t,e){switch(e.type){case"Download":const r=await Et(n,t,"pl",`pl-${e.version}`,P.arch(),P.platform());return u.join(r.baseName,"binaries",xt[_(P.platform())]);case"Local":return e.path;default:p.assertNever(e)}}const xt={linux:"platforma",macos:"platforma",windows:"platforma.exe"};function J(n){return u.join(n,"pl_pid")}async function Dt(n){if(!await p.fileExists(n))return;const t=await y.readFile(n);return Number(t.toString())}async function kt(n,t){await y.writeFile(n,JSON.stringify(t))}function _t(){return{}}function Rt(n,t,e){return n[t]=e,e}async function R(n,t){const e=_t();try{return await t((i,o)=>Rt(e,i,o),e)}catch(r){throw n.error(`error ${r} while doing traced operation, state: ${JSON.stringify(e)}`),r}}const V="config-local.yaml";class W{constructor(t,e,r,i,o,s,a,c){f(this,"instance");f(this,"pid");f(this,"nRuns",0);f(this,"lastRunHistory",{});f(this,"wasStopped",!1);this.logger=t,this.workingDir=e,this.startOptions=r,this.initialStartHistory=i,this.onClose=o,this.onError=s,this.onCloseAndError=a,this.onCloseAndErrorNoStop=c}async start(){await R(this.logger,async(t,e)=>{this.wasStopped=!1;const r=yt(this.logger,this.startOptions);r.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)}),r.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)}),t("started",!0);const i=t("pidFile",J(this.workingDir));t("pid",p.notEmpty(r.pid)),t("pidWritten",await kt(i,p.notEmpty(r.pid))),this.nRuns++,this.instance=r,this.pid=r.pid,this.lastRunHistory=e})}stop(){this.wasStopped=!0,G(p.notEmpty(this.pid))}async waitStopped(){await z(p.notEmpty(this.pid),15e3)}stopped(){return this.wasStopped}async isAlive(){return await k(p.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 Nt(n,t){const e=Math.max(mt.cpus().length-2,1),r=K(t,e);return await R(n,async(i,o)=>{i("startOptions",{...r,config:"too wordy"});const s=u.resolve(r.workingDir);r.closeOld&&i("closeOld",await Bt(n,s));const a=u.join(s,V);n.info(`writing configuration '${a}'...`),await y.writeFile(a,r.config);const c=u.join(s,"binaries"),l=await Ot(n,c,r.plBinary),d=i("binaryPath",u.join("binaries",l)),h=Y(d,a,r,s,process.env);i("processOpts",{cmd:h.cmd,args:h.args,cwd:h.opts.cwd});const m=new W(n,r.workingDir,h,o,r.onClose,r.onError,r.onCloseAndError,r.onCloseAndErrorNoStop);return await m.start(),m})}async function Bt(n,t){return await R(n,async(e,r)=>{const i=e("pidFilePath",J(t)),o=e("pid",await Dt(i)),s=e("wasAlive",await k(o));return o!==void 0&&s&&(e("stopped",G(o)),e("waitStopped",await z(o,1e4))),r})}function K(n,t){var i;const e={plBinary:At(),spawnOptions:{env:{GOMAXPROCS:String(t)}},closeOld:!0};if((i=n.spawnOptions)!=null&&i.env&&(e.spawnOptions.env={...e.spawnOptions.env,...n.spawnOptions.env}),n.spawnOptions){const o={...n.spawnOptions};delete o.env,e.spawnOptions={...e.spawnOptions,...o}}const r={...n};return delete r.spawnOptions,{...e,...r}}function Y(n,t,e,r,i){var a;const o={cmd:n,args:["--config",t],opts:{env:{...i},cwd:r,stdio:["pipe","ignore","inherit"],windowsHide:!0}};(a=e.spawnOptions)!=null&&a.env&&(o.opts.env={...o.opts.env,...e.spawnOptions.env});const s={...e.spawnOptions};return delete s.env,o.opts={...o.opts,...s},o}const It={keepaliveInterval:6e4,keepaliveCountMax:10};class O{constructor(t,e){f(this,"config");f(this,"homeDir");f(this,"forwardedServers",[]);this.logger=t,this.client=e}static async init(t,e){const r={...It,...e},i=new O(t,new C.Client);return await i.connect(r),i}getForwardedServers(){return this.forwardedServers}getFullHostName(){var t,e;return`${(t=this.config)==null?void 0:t.host}:${(e=this.config)==null?void 0:e.port}`}getUserName(){var t;return(t=this.config)==null?void 0:t.username}async connect(t){return this.config=t,await Ut(this.client,t)}async exec(t){return new Promise((e,r)=>{this.client.exec(t,(i,o)=>{if(i)return r(new Error(`ssh.exec: ${t}: ${i}`));let s="",a="";o.on("close",c=>{c===0?e({stdout:s,stderr:a}):r(new Error(`Command ${t} exited with code ${c}, stdout: ${s}, stderr: ${a}`))}).on("data",c=>{s+=c.toString()}).stderr.on("data",c=>{a+=c.toString()})})})}static async getAuthTypes(t,e){return new Promise(r=>{let i="";const o=new C.Client;o.on("ready",()=>{o.end();const s=this.extractAuthMethods(i);r(s.length===0?["publickey","password"]:s)}),o.on("error",()=>{o.end(),r(["publickey","password"])}),o.connect({host:t,port:e,username:new Date().getTime().toString(),debug:s=>{i+=`${s}
|
|
8
|
+
`}})})}static extractAuthMethods(t){const e=t.match(/Inbound: Received USERAUTH_FAILURE \((.+)\)/);return e&&e[1]?e[1].split(",").map(r=>r.trim()):[]}async forwardPort(t,e){const r=`ssh.forward:${t.localPort}:${t.remotePort}.id_${b.randomBytes(1).toString("hex")}`;e=e??this.config;const i=new p.RetryablePromise(o=>new Promise((s,a)=>{const c=new C.Client;c.on("ready",()=>{this.logger.info(`${r}.client.ready`),s(c)}),c.on("error",l=>{this.logger.info(`${r}.client.error: ${l}`),o.reset(),a(l)}),c.on("close",()=>{this.logger.info(`${r}.client.closed`),o.reset()}),c.connect(e)}));return await i.ensure(),new Promise((o,s)=>{const a=H.createServer({pauseOnConnect:!0},async c=>{const l=`${r}.sock_${b.randomBytes(1).toString("hex")}`;let d;try{d=await i.ensure()}catch(m){this.logger.info(`${l}.persistentClient.catch: ${m}`),c.end();return}d.setNoDelay(!0),c.setNoDelay(!0);let h;try{h=await Tt(this.logger,d,"127.0.0.1",0,"127.0.0.1",t.remotePort)}catch(m){this.logger.error(`${l}.forwardOut.err: ${m}`),c.end();return}c.pipe(h),h.pipe(c),c.resume(),h.on("error",m=>{this.logger.error(`${l}.stream.error: ${m}`),c.end(),h.end()}),h.on("close",()=>{c.end(),h.end()}),c.on("close",()=>{this.logger.info(`${l}.localSocket: closed`),c.end(),h.end()})});a.listen(t.localPort,"127.0.0.1",()=>{this.logger.info(`${r}.server: started listening`),this.forwardedServers.push(a),o({server:a})}),a.on("error",c=>{a.close(),s(new Error(`${r}.server: error: ${JSON.stringify(c)}`))}),a.on("close",()=>{this.logger.info(`${r}.server: closed ${JSON.stringify(t)}`),this.forwardedServers=this.forwardedServers.filter(c=>c!==a)})})}closeForwardedPorts(){this.logger.info("[SSH] Closing all forwarded ports..."),this.forwardedServers.forEach(t=>{const e=t.address();if(e&&typeof e!="string"){const r=e;this.logger.info(`[SSH] Closing port forward for server ${r.address}:${r.port}`)}t.close()}),this.forwardedServers=[]}static async checkHostAvailability(t){return new Promise(e=>{ft.lookup(t,r=>{e(!r)})})}static async isPassphraseRequiredForKey(t){return new Promise((e,r)=>{try{return C.utils.parseKey(t)instanceof Error&&e(!0),e(!1)}catch(i){console.log("Error parsing privateKey"),r(new Error(`ssh.isPassphraseRequiredForKey: err ${i}`))}})}async uploadFile(t,e){return await this.withSftp(async r=>new Promise((i,o)=>{r.fastPut(t,e,s=>{if(s){const a=new Error(`ssh.uploadFile: err: ${s}, localPath: ${t}, remotePath: ${e}`);return o(a)}i(!0)})}))}async withSftp(t){return new Promise((e,r)=>{this.client.sftp((i,o)=>{if(i)return r(new Error(`ssh.withSftp: sftp err: ${i}`));t(o).then(e).catch(s=>{r(new Error(`ssh.withSftp.callback: err ${s}`))}).finally(()=>{o==null||o.end()})})})}async writeFileOnTheServer(t,e,r=432){return this.withSftp(async i=>this.writeFile(i,t,e,r))}async getForderStructure(t,e,r={files:[],directories:[]}){return new Promise((i,o)=>{t.readdir(e,async(s,a)=>{if(s)return o(s);for(const c of a){const l=`${e}/${c.filename}`;if(c.attrs.isDirectory()){r.directories.push(l);try{await this.getForderStructure(t,l,r)}catch(d){return o(d instanceof Error?d:new Error(String(d)))}}else r.files.push(l)}i(r)})})}rmdir(t,e){return new Promise((r,i)=>{t.rmdir(e,o=>o?i(o):r(!0))})}unlink(t,e){return new Promise((r,i)=>{t.unlink(e,o=>o?i(o):r(!0))})}async deleteFolder(t){return this.withSftp(async e=>{try{const r=await this.getForderStructure(e,t);this.logger.info("ssh.deleteFolder list of files and directories"),this.logger.info(`ssh.deleteFolder list of files: ${r.files}`),this.logger.info(`ssh.deleteFolder list of directories: ${r.directories}`);for(const i of r.files)this.logger.info(`ssh.deleteFolder unlink file ${i}`),await this.unlink(e,i);r.directories.sort((i,o)=>o.length-i.length);for(const i of r.directories)this.logger.info(`ssh.deleteFolder rmdir ${i}`),await this.rmdir(e,i);return await this.rmdir(e,t),!0}catch(r){this.logger.error(r);const i=r instanceof Error?r.message:"";throw new Error(`ssh.deleteFolder: path: ${t}, message: ${i}`)}})}async readFile(t){return this.withSftp(async e=>new Promise((r,i)=>{e.readFile(t,(o,s)=>{if(o)return i(new Error(`ssh.readFile: ${o}`));r(s.toString())})}))}async chmod(t,e){return this.withSftp(async r=>new Promise((i,o)=>{r.chmod(t,e,s=>s?o(new Error(`ssh.chmod: ${s}, path: ${t}, mode: ${e}`)):i(void 0))}))}async checkFileExists(t){return this.withSftp(async e=>new Promise((r,i)=>{e.stat(t,(o,s)=>{if(o)return(o==null?void 0:o.code)===2?r(!1):i(new Error(`ssh.checkFileExists: err ${o}`));r(s.isFile())})}))}async checkPathExists(t){return this.withSftp(async e=>new Promise((r,i)=>{e.stat(t,(o,s)=>{if(o)return o.code===2?r({exists:!1,isFile:!1,isDirectory:!1}):i(new Error(`ssh.checkPathExists: ${o}`));r({exists:!0,isFile:s.isFile(),isDirectory:s.isDirectory()})})}))}async writeFile(t,e,r,i=432){return new Promise((o,s)=>{t.writeFile(e,r,{mode:i},a=>{if(a)return s(new Error(`ssh.writeFile: err ${a}, remotePath: ${e}`));o(!0)})})}uploadFileUsingExistingSftp(t,e,r,i=432){return new Promise((o,s)=>{y.readFile(e).then(async a=>this.writeFile(t,r,a,i).then(()=>{o(void 0)}).catch(c=>{const l=`uploadFileUsingExistingSftp: ${c}`;this.logger.error(l),s(new Error(l))}))})}async __uploadDirectory(t,e,r,i=432){return new Promise((o,s)=>{D.readdir(e,async(a,c)=>{if(a)return s(new Error(`ssh.__uploadDir: err ${a}, localDir: ${e}, remoteDir: ${r}`));try{await this.__createRemoteDirectory(t,r);for(const l of c){const d=u.join(e,l),h=`${r}/${l}`;D.lstatSync(d).isDirectory()?await this.__uploadDirectory(t,d,h,i):await this.uploadFileUsingExistingSftp(t,d,h,i)}o()}catch(l){const d=`ssh.__uploadDir: catched err ${l}`;this.logger.error(d),s(new Error(d))}})})}async uploadDirectory(t,e,r=432){return new Promise((i,o)=>{this.withSftp(async s=>{try{await this.__uploadDirectory(s,t,e,r),i()}catch(a){o(new Error(`ssh.uploadDirectory: ${a}`))}})})}__createRemoteDirectory(t,e){return new Promise((r,i)=>{const o=e.split("/");let s="";const a=c=>{if(c>=o.length)return r();s+=`${o[c]}/`,t.stat(s,l=>{l?t.mkdir(s,d=>{if(d)return i(new Error(`ssh.__createRemDir: err ${d}, remotePath: ${e}`));a(c+1)}):a(c+1)})};a(0)})}ensureRemoteDirCreated(t,e=493){return this.withSftp(async r=>{const i=t.split("/");let o="";for(const s of i){o+=`${s}/`;try{await new Promise((a,c)=>{r.stat(o,l=>{if(!l)return a();r.mkdir(o,{mode:e},d=>{if(d)return c(new Error(`ssh.createRemoteDir: err ${d}, remotePath: ${t}`));a()})})})}catch(a){throw console.error(`Failed to create directory: ${o}`,a),a}}})}async downloadFile(t,e){return this.withSftp(async r=>new Promise((i,o)=>{r.fastGet(t,e,s=>{if(s)return o(new Error(`ssh.downloadFile: err ${s}, remotePath: ${t}, localPath: ${e}`));i(!0)})}))}close(){this.closeForwardedPorts(),this.client.end()}}async function Ut(n,t,e,r){return new Promise((i,o)=>{n.on("ready",()=>{i(n)}),n.on("error",s=>{o(new Error(`ssh.connect: ${s}`))}),n.on("close",()=>{}),n.connect(t),n.setNoDelay(!0)})}async function Tt(n,t,e,r,i,o){return new Promise((s,a)=>{t.forwardOut(e,r,i,o,(c,l)=>c?(n.error(`forwardOut.error: ${c}`),a(c)):s(l))})}const jt="minio-2024-12-18T13-15-44Z",Mt="supervisord-0.7.3",Ht="supervisord_0.7.3_Linux_64-bit";function g(n){return u.join(n,".platforma_ssh")}function x(n){return u.join(g(n),"binaries")}function qt(n,t){return u.join(x(n),`pl-${A()}-${S(t)}`)}function Z(n,t){return u.join(qt(n,t),"binaries")}function U(n,t){return u.join(Z(n,t),"platforma")}function Gt(n,t){return u.join(Z(n,t),"free-port")}function X(n,t){return u.join(x(n),`minio-2024-12-18T13-15-44Z-${S(t)}`)}function zt(n,t){return u.join(X(n,t),"minio")}function Lt(n,t){return u.join(x(n),`supervisord-0.7.3-${S(t)}`,Ht)}function Q(n,t){return u.join(Lt(n,t),"supervisord")}function tt(n){return u.join(g(n),"supervisor.conf")}function T(n){return u.join(g(n),"connection.txt")}function Jt(n){return u.join(g(n),"platforma_cli_logs.log")}async function Vt(n,t,e){const r=await N(n,t,e,"--daemon");if(r.stderr)throw new Error(`Can not run ssh Platforma ${r.stderr}`)}async function Wt(n,t,e){const r=await N(n,t,e,"ctl shutdown");if(r.stderr)throw new Error(`Can not stop ssh Platforma ${r.stderr}`)}function E(n,t){return t?n.platforma&&n.minio:n.platforma}function Kt(n){return n.execError===void 0}async function Yt(n,t,e,r){let i;try{i=await N(t,e,r,"ctl status")}catch(c){return{execError:String(c)}}if(i.stderr)return n.info(`supervisord ctl status: stderr occurred: ${i.stderr}, stdout: ${i.stdout}`),{rawResult:i};const o=j(i.stdout,"platforma"),s=j(i.stdout,"minio"),a={rawResult:i,platforma:o,minio:s};return a.minio||n.warn("Minio is not running on the server"),a.platforma||n.warn("Platforma is not running on the server"),a}function Zt(n,t,e,r){const i=b.randomBytes(16).toString("hex"),o=n;return`
|
|
9
9
|
[supervisord]
|
|
10
10
|
logfile=${t}/supervisord.log
|
|
11
11
|
loglevel=info
|
|
@@ -26,7 +26,11 @@ autostart=true
|
|
|
26
26
|
command=${r} --config ${e}
|
|
27
27
|
directory=${t}
|
|
28
28
|
autorestart=true
|
|
29
|
-
|
|
29
|
+
stdout_logfile=${t}/platforma_cli_logs.log
|
|
30
|
+
stdout_logfile_maxbytes=10000
|
|
31
|
+
stdout_logfile_backups=10
|
|
32
|
+
redirect_stderr=true
|
|
33
|
+
`}function Xt(n,t,e,r,i,o,s){const a=Object.entries(t).map(([d,h])=>`${d}="${h}"`).join(","),c=b.randomBytes(16).toString("hex"),l=e;return`
|
|
30
34
|
[supervisord]
|
|
31
35
|
logfile=${r}/supervisord.log
|
|
32
36
|
loglevel=info
|
|
@@ -55,7 +59,7 @@ environment=${a}
|
|
|
55
59
|
command=${o} server ${n}
|
|
56
60
|
directory=${r}
|
|
57
61
|
autorestart=true
|
|
58
|
-
`}async function
|
|
59
|
-
`).some(i=>{const[o,s]=i.trim().split(/\s{2,}/);return o===t&&s==="Running"})}const v=f.z.object({local:f.z.number(),remote:f.z.number()}),et=f.z.object({grpc:v,http:v.optional(),monitoring:v,debug:v,minioPort:v,minioConsolePort:v}),rt=f.z.object({plUser:f.z.string(),plPassword:f.z.string(),ports:et,useGlobalAccess:f.z.boolean().default(!1),plVersion:f.z.string().default("1.18.3"),minioIsUsed:f.z.boolean().default(!0)});function it(n,t,e,r,i,o){return{plUser:n,plPassword:t,ports:e,useGlobalAccess:r,plVersion:i,minioIsUsed:o}}function nt(n){return rt.parse(JSON.parse(n))}function ot(n){return JSON.stringify(n,void 0,2)}const M=2.28;class I{constructor(t,e,r){w(this,"initState",{step:"init"});this.logger=t,this.sshClient=e,this.username=r}info(){return{username:this.username,initState:this.initState}}static async init(t,e){try{const r=await D.init(t,e);return new I(t,r,p.notEmpty(e.username))}catch(r){throw t.error(`Connection error in SshClient.init: ${r}`),r}}cleanUp(){this.sshClient.close()}async isAlive(){const t=await this.getArch(),e=await this.getUserHomeDirectory();return await Kt(this.logger,this.sshClient,e,t.arch)}async start(t){const e=await this.getArch(),r=await this.getUserHomeDirectory();try{if(!E(await this.isAlive(),t))return await Lt(this.sshClient,r,e.arch),await this.checkIsAliveWithInterval(t)}catch(i){const o=`SshPl.start: ${i}`;throw this.logger.error(o),new Error(o)}}async stop(){const t=await this.getArch(),e=await this.getUserHomeDirectory();try{const r=await this.isAlive();if(Wt(r)){await Vt(this.sshClient,e,t.arch);const i=r.minio===!0;return await this.checkIsAliveWithInterval(i,1e3,15,!1)}}catch(r){const i=`PlSsh.stop: ${r}`;throw this.logger.error(i),new Error(i)}}async reset(){return await this.stopAndClean(),this.cleanUp(),!0}async stopAndClean(){const t=await this.getUserHomeDirectory();this.logger.info("pl.reset: Stop Platforma on the server"),await this.stop(),this.logger.info(`pl.reset: Deleting Platforma workDir ${$(t)} on the server`),await this.sshClient.deleteFolder($(t))}async platformaInit(t){const e={localWorkdir:t.localWorkdir,step:"init"},{onProgress:r}=t,i={...Xt,...t};e.plBinaryOps=i.plBinary;try{return await this.doStepDetectArch(e,r),await this.doStepDetectHome(e,r),await this.doStepReadExistedConfig(e,i,r)?(await this.doStepStopExistedPlatforma(e,r),await(r==null?void 0:r("Installation platforma...")),await this.doStepDownloadBinaries(e,r,i),await this.doStepFetchPorts(e),await this.doStepGenerateNewConfig(e,r,i),await this.doStepCreateFoldersAndSaveFiles(e,r),await this.doStepConfigureSupervisord(e,r),await this.doStepSaveNewConnectionInfo(e,r,i),await this.doStepStartPlatforma(e,r),e.connectionInfo):(await(r==null?void 0:r("Platforma is already running. Skipping initialization.")),e.existedSettings)}catch(o){const s=`SshPl.platformaInit: ${o}, state: ${JSON.stringify(this.removeSensitiveData(e))}`;throw this.logger.error(s),new Error(s)}}async doStepStopExistedPlatforma(t,e){t.step="stopExistedPlatforma",E(t.alive,t.shouldUseMinio??!1)&&(await(e==null?void 0:e("Stopping services...")),await this.stop())}removeSensitiveData(t){const e={...t};return e.generatedConfig={...e.generatedConfig,filesToCreate:{skipped:"sanitized"}},e}async doStepStartPlatforma(t,e){t.step="startPlatforma",await(e==null?void 0:e("Starting Platforma on the server...")),await this.start(t.shouldUseMinio??!1),t.started=!0,this.initState=t,await(e==null?void 0:e("Platforma has been started successfully."))}async doStepSaveNewConnectionInfo(t,e,r){t.step="saveNewConnectionInfo";const i=t.generatedConfig;await(e==null?void 0:e("Saving connection information...")),t.connectionInfo=it(i.plUser,i.plPassword,t.ports,p.notEmpty(r.useGlobalAccess),r.plBinary.version,t.shouldUseMinio??!1),await this.sshClient.writeFileOnTheServer(T(t.remoteHome),ot(t.connectionInfo)),await(e==null?void 0:e("Connection information saved."))}async doStepConfigureSupervisord(t,e){await(e==null?void 0:e("Writing supervisord configuration...")),t.step="configureSupervisord";const r=t.generatedConfig;let i;if(t.shouldUseMinio?i=Zt(r.minioConfig.storageDir,r.minioConfig.envs,await this.getFreePortForPlatformaOnServer(t.remoteHome,t.arch),r.workingDir,r.plConfig.configPath,t.binPaths.minioRelPath,t.binPaths.downloadedPl):i=Yt(await this.getFreePortForPlatformaOnServer(t.remoteHome,t.arch),r.workingDir,r.plConfig.configPath,t.binPaths.downloadedPl),!await this.sshClient.writeFileOnTheServer(tt(t.remoteHome),i))throw new Error(`Can not write supervisord config on the server ${$(t.remoteHome)}`);await(e==null?void 0:e("Supervisord configuration written."))}async doStepCreateFoldersAndSaveFiles(t,e){t.step="createFoldersAndSaveFiles";const r=t.generatedConfig;await(e==null?void 0:e("Generating folder structure..."));for(const[i,o]of Object.entries(r.filesToCreate))await this.sshClient.writeFileOnTheServer(i,o),this.logger.info(`Created file ${i}`);for(const i of r.dirsToCreate)await this.sshClient.ensureRemoteDirCreated(i),this.logger.info(`Created directory ${i}`);await(e==null?void 0:e("Folder structure created."))}async doStepGenerateNewConfig(t,e,r){t.step="generateNewConfig",await(e==null?void 0:e("Generating new config..."));const i=await g.generateSshPlConfigs({logger:this.logger,workingDir:$(t.remoteHome),portsMode:{type:"customWithMinio",ports:{debug:t.ports.debug.remote,grpc:t.ports.grpc.remote,http:t.ports.http.remote,minio:t.ports.minioPort.remote,minioConsole:t.ports.minioConsolePort.remote,monitoring:t.ports.monitoring.remote,httpLocal:t.ports.http.local,grpcLocal:t.ports.grpc.local,minioLocal:t.ports.minioPort.local}},licenseMode:r.license,useGlobalAccess:p.notEmpty(r.useGlobalAccess),plConfigPostprocessing:r.plConfigPostprocessing,useMinio:t.shouldUseMinio??!1});t.generatedConfig={...i},await(e==null?void 0:e("New config generated"))}async doStepFetchPorts(t){var e;if(t.step="fetchPorts",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||!((e=t.ports.http)!=null&&e.remote))throw new Error("SshPl.platformaInit: remote ports are not defined")}async doStepDownloadBinaries(t,e,r){t.step="downloadBinaries",await(e==null?void 0:e("Downloading and uploading required binaries..."));const i=await Qt(this.logger,this.sshClient);if(i<M)throw new Error(`glibc version ${i} is too old. Version ${M} or higher is required for Platforma.`);const o=await this.downloadBinariesAndUploadToTheServer(r.localWorkdir,r.plBinary,t.remoteHome,t.arch,t.shouldUseMinio??!1);await(e==null?void 0:e("All required binaries have been downloaded and uploaded.")),t.binPaths={...o,history:void 0},t.downloadedBinaries=o.history}async doStepDetectArch(t,e){t.step="detectArch",await(e==null?void 0:e("Detecting server architecture...")),t.arch=await this.getArch(),await(e==null?void 0:e("Server architecture detected."))}async doStepDetectHome(t,e){t.step="detectHome",await(e==null?void 0:e("Fetching user home directory...")),t.remoteHome=await this.getUserHomeDirectory(),await(e==null?void 0:e("User home directory retrieved."))}async doStepReadExistedConfig(t,e,r){var s;if(t.step="checkAlive",await(r==null?void 0:r("Checking platform status...")),t.alive=await this.isAlive(),!((s=t.alive)!=null&&s.platforma))return!0;if(await(r==null?void 0:r("All required services are running.")),t.existedSettings=await this.readExistedConfig(t.remoteHome),!t.existedSettings)throw new Error("SshPl.platformaInit: platforma is alive but existed settings are not found");const i=t.existedSettings.useGlobalAccess==e.useGlobalAccess,o=t.existedSettings.plVersion==e.plBinary.version;return t.needRestart=!(i&&o),this.logger.info(`SshPl.platformaInit: need restart? ${t.needRestart}`),t.shouldUseMinio=t.existedSettings.minioIsUsed,t.shouldUseMinio?this.logger.info("SshPl.platformaInit: minio is used"):this.logger.info("SshPl.platformaInit: minio is not used"),t.needRestart?(await(r==null?void 0:r("Stopping services...")),await this.stop(),!0):(await(r==null?void 0:r("Server setup completed.")),!1)}async downloadBinariesAndUploadToTheServer(t,e,r,i,o){const s=[];try{const a=await this.downloadAndUntar(t,r,i,"pl",`pl-${e.version}`);s.push(a);const c=await this.downloadAndUntar(t,r,i,"supervisord",Mt);s.push(c);const l=zt(r,i.arch);if(o){const d=await this.downloadAndUntar(t,r,i,"minio",jt);s.push(d),await this.sshClient.chmod(l,488)}return{history:s,minioRelPath:o?l:void 0,downloadedPl:_(r,i.arch)}}catch(a){const c=`SshPl.downloadBinariesAndUploadToServer: ${a}, state: ${JSON.stringify(s)}`;throw this.logger.error(c),a}}async downloadAndUntar(t,e,r,i,o){const s={};s.binBasePath=O(e),await this.sshClient.ensureRemoteDirCreated(s.binBasePath),s.binBasePathCreated=!0;let a=null;const c=5;for(let d=1;d<=c;d++)try{a=await Ct(this.logger,t,i,o,r.arch,r.platform);break}catch(h){if(await p.sleep(300),d==c)throw new Error(`downloadAndUntar: ${c} attempts, last error: ${h}`)}s.downloadResult=p.notEmpty(a),s.localArchivePath=u.resolve(s.downloadResult.archivePath),s.remoteDir=u.join(s.binBasePath,s.downloadResult.baseName),s.remoteArchivePath=s.remoteDir+".tgz",await this.sshClient.ensureRemoteDirCreated(s.remoteDir),await this.sshClient.uploadFile(s.localArchivePath,s.remoteArchivePath),s.uploadDone=!0;try{await this.sshClient.exec("hash tar")}catch{throw new Error("tar is not installed on the server. Please install it before running Platforma.")}const l=await this.sshClient.exec(`tar --warning=no-all -xvf ${s.remoteArchivePath} --directory=${s.remoteDir}`);if(l.stderr)throw new Error(`downloadAndUntar: untar: stderr occurred: ${l.stderr}, stdout: ${l.stdout}`);return s.untarDone=!0,s}async needDownload(t,e){const r=Q(t,e.arch),i=X(t,e.arch),o=_(t,e.arch);return!await this.sshClient.checkFileExists(o)||!await this.sshClient.checkFileExists(i)||!await this.sshClient.checkFileExists(r)}async checkIsAliveWithInterval(t,e=1e3,r=15,i=!0){const o=r*e;let s=0,a=await this.isAlive();for(;i?!E(a,t):E(a,t);){if(await p.sleep(e),s+=e,s>o)throw new Error(`isAliveWithInterval: The process did not ${i?"started":"stopped"} after ${o} ms. Live status: ${JSON.stringify(a)}`);a=await this.isAlive()}}async readExistedConfig(t){const e=await this.sshClient.readFile(T(t));return nt(e)}async fetchPorts(t,e){return{grpc:{local:await g.getFreePort(),remote:await this.getFreePortForPlatformaOnServer(t,e)},monitoring:{local:await g.getFreePort(),remote:await this.getFreePortForPlatformaOnServer(t,e)},debug:{local:await g.getFreePort(),remote:await this.getFreePortForPlatformaOnServer(t,e)},http:{local:await g.getFreePort(),remote:await this.getFreePortForPlatformaOnServer(t,e)},minioPort:{local:await g.getFreePort(),remote:await this.getFreePortForPlatformaOnServer(t,e)},minioConsolePort:{local:await g.getFreePort(),remote:await this.getFreePortForPlatformaOnServer(t,e)}}}async getLocalFreePort(){return new Promise(t=>{const e=H.createServer();e.listen(0,()=>{const r=e.address().port;e.close(i=>t(r))})})}async getFreePortForPlatformaOnServer(t,e){const r=Gt(t,e.arch),{stdout:i,stderr:o}=await this.sshClient.exec(`${r}`);if(o)throw new Error(`getFreePortForPlatformaOnServer: stderr is not empty: ${o}, stdout: ${i}`);return+i}async getArch(){const{stdout:t,stderr:e}=await this.sshClient.exec("uname -s && uname -m");if(e)throw new Error(`getArch: stderr is not empty: ${e}, stdout: ${t}`);const r=t.split(`
|
|
60
|
-
`);return{platform:r[0],arch:r[1]}}async getUserHomeDirectory(){const{stdout:t,stderr:e}=await this.sshClient.exec("echo $HOME");if(e){const r=`/home/${this.username}`;return console.warn(`getUserHomeDirectory: stderr is not empty: ${e}, stdout: ${t}, will get a default home: ${r}`),r}return t.trim()}}const
|
|
62
|
+
`}async function N(n,t,e,r){const i=Q(t,e),o=tt(t),s=`${i} --configuration ${o} ${r}`;return await n.exec(s)}function j(n,t){return(i=>i.replace(/\x1B\[[0-9;]*m/g,""))(n).split(`
|
|
63
|
+
`).some(i=>{const[o,s]=i.trim().split(/\s{2,}/);return o===t&&s==="Running"})}const $=w.z.object({local:w.z.number(),remote:w.z.number()}),et=w.z.object({grpc:$,http:$.optional(),monitoring:$,debug:$,minioPort:$,minioConsolePort:$}),rt=w.z.object({plUser:w.z.string(),plPassword:w.z.string(),ports:et,useGlobalAccess:w.z.boolean().default(!1),plVersion:w.z.string().default("1.18.3"),minioIsUsed:w.z.boolean().default(!0)});function it(n,t,e,r,i,o){return{plUser:n,plPassword:t,ports:e,useGlobalAccess:r,plVersion:i,minioIsUsed:o}}function nt(n){return rt.parse(JSON.parse(n))}function ot(n){return JSON.stringify(n,void 0,2)}const M=2.28;class B{constructor(t,e,r){f(this,"initState",{step:"init"});this.logger=t,this.sshClient=e,this.username=r}info(){return{username:this.username,initState:this.initState}}static async init(t,e){try{const r=await O.init(t,e);return new B(t,r,p.notEmpty(e.username))}catch(r){throw t.error(`Connection error in SshClient.init: ${r}`),r}}cleanUp(){this.sshClient.close()}async isAlive(){const t=await this.getArch(),e=await this.getUserHomeDirectory();return await Yt(this.logger,this.sshClient,e,t.arch)}async start(t){const e=await this.getArch(),r=await this.getUserHomeDirectory();try{if(!E(await this.isAlive(),t))return await Vt(this.sshClient,r,e.arch),await this.checkIsAliveWithInterval(t)}catch(i){let o=`SshPl.start: ${i}`,s="";try{s=await this.sshClient.readFile(Jt(r)),o+=`, platforma cli logs: ${s}`}catch(a){o+=`, Can not read platforma cli logs: ${a}`}throw this.logger.error(o),new Error(o)}}async stop(){const t=await this.getArch(),e=await this.getUserHomeDirectory();try{const r=await this.isAlive();if(Kt(r)){await Wt(this.sshClient,e,t.arch);const i=r.minio===!0;return await this.checkIsAliveWithInterval(i,1e3,15,!1)}}catch(r){const i=`PlSsh.stop: ${r}`;throw this.logger.error(i),new Error(i)}}async reset(){return await this.stopAndClean(),this.cleanUp(),!0}async stopAndClean(){const t=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(t)} on the server`),await this.sshClient.deleteFolder(g(t))}async platformaInit(t){const e={localWorkdir:t.localWorkdir,step:"init"},{onProgress:r}=t,i={...Qt,...t};e.plBinaryOps=i.plBinary;try{return await this.doStepDetectArch(e,r),await this.doStepDetectHome(e,r),await this.doStepReadExistedConfig(e,i,r)?(await this.doStepStopExistedPlatforma(e,r),await(r==null?void 0:r("Installation platforma...")),await this.doStepDownloadBinaries(e,r,i),await this.doStepFetchPorts(e),await this.doStepGenerateNewConfig(e,r,i),await this.doStepCreateFoldersAndSaveFiles(e,r),await this.doStepConfigureSupervisord(e,r),await this.doStepSaveNewConnectionInfo(e,r,i),await this.doStepStartPlatforma(e,r),e.connectionInfo):(await(r==null?void 0:r("Platforma is already running. Skipping initialization.")),e.existedSettings)}catch(o){const s=`SshPl.platformaInit: ${o}, state: ${JSON.stringify(this.removeSensitiveData(e))}`;throw this.logger.error(s),new Error(s)}}async doStepStopExistedPlatforma(t,e){t.step="stopExistedPlatforma",E(t.alive,t.shouldUseMinio??!1)&&(await(e==null?void 0:e("Stopping services...")),await this.stop())}removeSensitiveData(t){const e={...t};return e.generatedConfig={...e.generatedConfig,filesToCreate:{skipped:"sanitized"}},e}async doStepStartPlatforma(t,e){t.step="startPlatforma",await(e==null?void 0:e("Starting Platforma on the server...")),await this.start(t.shouldUseMinio??!1),t.started=!0,this.initState=t,await(e==null?void 0:e("Platforma has been started successfully."))}async doStepSaveNewConnectionInfo(t,e,r){t.step="saveNewConnectionInfo";const i=t.generatedConfig;await(e==null?void 0:e("Saving connection information...")),t.connectionInfo=it(i.plUser,i.plPassword,t.ports,p.notEmpty(r.useGlobalAccess),r.plBinary.version,t.shouldUseMinio??!1),await this.sshClient.writeFileOnTheServer(T(t.remoteHome),ot(t.connectionInfo)),await(e==null?void 0:e("Connection information saved."))}async doStepConfigureSupervisord(t,e){await(e==null?void 0:e("Writing supervisord configuration...")),t.step="configureSupervisord";const r=t.generatedConfig;let i;if(t.shouldUseMinio?i=Xt(r.minioConfig.storageDir,r.minioConfig.envs,await this.getFreePortForPlatformaOnServer(t.remoteHome,t.arch),r.workingDir,r.plConfig.configPath,t.binPaths.minioRelPath,t.binPaths.downloadedPl):i=Zt(await this.getFreePortForPlatformaOnServer(t.remoteHome,t.arch),r.workingDir,r.plConfig.configPath,t.binPaths.downloadedPl),!await this.sshClient.writeFileOnTheServer(tt(t.remoteHome),i))throw new Error(`Can not write supervisord config on the server ${g(t.remoteHome)}`);await(e==null?void 0:e("Supervisord configuration written."))}async doStepCreateFoldersAndSaveFiles(t,e){t.step="createFoldersAndSaveFiles";const r=t.generatedConfig;await(e==null?void 0:e("Generating folder structure..."));for(const[i,o]of Object.entries(r.filesToCreate))await this.sshClient.writeFileOnTheServer(i,o),this.logger.info(`Created file ${i}`);for(const i of r.dirsToCreate)await this.sshClient.ensureRemoteDirCreated(i),this.logger.info(`Created directory ${i}`);await(e==null?void 0:e("Folder structure created."))}async doStepGenerateNewConfig(t,e,r){t.step="generateNewConfig",await(e==null?void 0:e("Generating new config..."));const i=await v.generateSshPlConfigs({logger:this.logger,workingDir:g(t.remoteHome),portsMode:{type:"customWithMinio",ports:{debug:t.ports.debug.remote,grpc:t.ports.grpc.remote,http:t.ports.http.remote,minio:t.ports.minioPort.remote,minioConsole:t.ports.minioConsolePort.remote,monitoring:t.ports.monitoring.remote,httpLocal:t.ports.http.local,grpcLocal:t.ports.grpc.local,minioLocal:t.ports.minioPort.local}},licenseMode:r.license,useGlobalAccess:p.notEmpty(r.useGlobalAccess),plConfigPostprocessing:r.plConfigPostprocessing,useMinio:t.shouldUseMinio??!1});t.generatedConfig={...i},await(e==null?void 0:e("New config generated"))}async doStepFetchPorts(t){var e;if(t.step="fetchPorts",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||!((e=t.ports.http)!=null&&e.remote))throw new Error("SshPl.platformaInit: remote ports are not defined")}async doStepDownloadBinaries(t,e,r){t.step="downloadBinaries",await(e==null?void 0:e("Downloading and uploading required binaries..."));const i=await te(this.logger,this.sshClient);if(i<M)throw new Error(`glibc version ${i} is too old. Version ${M} or higher is required for Platforma.`);const o=await this.downloadBinariesAndUploadToTheServer(r.localWorkdir,r.plBinary,t.remoteHome,t.arch,t.shouldUseMinio??!1);await(e==null?void 0:e("All required binaries have been downloaded and uploaded.")),t.binPaths={...o,history:void 0},t.downloadedBinaries=o.history}async doStepDetectArch(t,e){t.step="detectArch",await(e==null?void 0:e("Detecting server architecture...")),t.arch=await this.getArch(),await(e==null?void 0:e("Server architecture detected."))}async doStepDetectHome(t,e){t.step="detectHome",await(e==null?void 0:e("Fetching user home directory...")),t.remoteHome=await this.getUserHomeDirectory(),await(e==null?void 0:e("User home directory retrieved."))}async doStepReadExistedConfig(t,e,r){var s;if(t.step="checkAlive",await(r==null?void 0:r("Checking platform status...")),t.alive=await this.isAlive(),!((s=t.alive)!=null&&s.platforma))return!0;if(await(r==null?void 0:r("All required services are running.")),t.existedSettings=await this.readExistedConfig(t.remoteHome),!t.existedSettings)throw new Error("SshPl.platformaInit: platforma is alive but existed settings are not found");const i=t.existedSettings.useGlobalAccess==e.useGlobalAccess,o=t.existedSettings.plVersion==e.plBinary.version;return t.needRestart=!(i&&o),this.logger.info(`SshPl.platformaInit: need restart? ${t.needRestart}`),t.shouldUseMinio=t.existedSettings.minioIsUsed,t.shouldUseMinio?this.logger.info("SshPl.platformaInit: minio is used"):this.logger.info("SshPl.platformaInit: minio is not used"),t.needRestart?(await(r==null?void 0:r("Stopping services...")),await this.stop(),!0):(await(r==null?void 0:r("Server setup completed.")),!1)}async downloadBinariesAndUploadToTheServer(t,e,r,i,o){const s=[];try{const a=await this.downloadAndUntar(t,r,i,"pl",`pl-${e.version}`);s.push(a);const c=await this.downloadAndUntar(t,r,i,"supervisord",Mt);s.push(c);const l=zt(r,i.arch);if(o){const d=await this.downloadAndUntar(t,r,i,"minio",jt);s.push(d),await this.sshClient.chmod(l,488)}return{history:s,minioRelPath:o?l:void 0,downloadedPl:U(r,i.arch)}}catch(a){const c=`SshPl.downloadBinariesAndUploadToServer: ${a}, state: ${JSON.stringify(s)}`;throw this.logger.error(c),a}}async downloadAndUntar(t,e,r,i,o){const s={};s.binBasePath=x(e),await this.sshClient.ensureRemoteDirCreated(s.binBasePath),s.binBasePathCreated=!0;let a=null;const c=5;for(let d=1;d<=c;d++)try{a=await Ct(this.logger,t,i,o,r.arch,r.platform);break}catch(h){if(await p.sleep(300),d==c)throw new Error(`downloadAndUntar: ${c} attempts, last error: ${h}`)}s.downloadResult=p.notEmpty(a),s.localArchivePath=u.resolve(s.downloadResult.archivePath),s.remoteDir=u.join(s.binBasePath,s.downloadResult.baseName),s.remoteArchivePath=s.remoteDir+".tgz",await this.sshClient.ensureRemoteDirCreated(s.remoteDir),await this.sshClient.uploadFile(s.localArchivePath,s.remoteArchivePath),s.uploadDone=!0;try{await this.sshClient.exec("hash tar")}catch{throw new Error("tar is not installed on the server. Please install it before running Platforma.")}const l=await this.sshClient.exec(`tar --warning=no-all -xvf ${s.remoteArchivePath} --directory=${s.remoteDir}`);if(l.stderr)throw new Error(`downloadAndUntar: untar: stderr occurred: ${l.stderr}, stdout: ${l.stdout}`);return s.untarDone=!0,s}async needDownload(t,e){const r=Q(t,e.arch),i=X(t,e.arch),o=U(t,e.arch);return!await this.sshClient.checkFileExists(o)||!await this.sshClient.checkFileExists(i)||!await this.sshClient.checkFileExists(r)}async checkIsAliveWithInterval(t,e=1e3,r=15,i=!0){const o=r*e;let s=0,a=await this.isAlive();for(;i?!E(a,t):E(a,t);){if(await p.sleep(e),s+=e,s>o)throw new Error(`isAliveWithInterval: The process did not ${i?"started":"stopped"} after ${o} ms. Live status: ${JSON.stringify(a)}`);a=await this.isAlive()}}async readExistedConfig(t){const e=await this.sshClient.readFile(T(t));return nt(e)}async fetchPorts(t,e){return{grpc:{local:await v.getFreePort(),remote:await this.getFreePortForPlatformaOnServer(t,e)},monitoring:{local:await v.getFreePort(),remote:await this.getFreePortForPlatformaOnServer(t,e)},debug:{local:await v.getFreePort(),remote:await this.getFreePortForPlatformaOnServer(t,e)},http:{local:await v.getFreePort(),remote:await this.getFreePortForPlatformaOnServer(t,e)},minioPort:{local:await v.getFreePort(),remote:await this.getFreePortForPlatformaOnServer(t,e)},minioConsolePort:{local:await v.getFreePort(),remote:await this.getFreePortForPlatformaOnServer(t,e)}}}async getLocalFreePort(){return new Promise(t=>{const e=H.createServer();e.listen(0,()=>{const r=e.address().port;e.close(i=>t(r))})})}async getFreePortForPlatformaOnServer(t,e){const r=Gt(t,e.arch),{stdout:i,stderr:o}=await this.sshClient.exec(`${r}`);if(o)throw new Error(`getFreePortForPlatformaOnServer: stderr is not empty: ${o}, stdout: ${i}`);return+i}async getArch(){const{stdout:t,stderr:e}=await this.sshClient.exec("uname -s && uname -m");if(e)throw new Error(`getArch: stderr is not empty: ${e}, stdout: ${t}`);const r=t.split(`
|
|
64
|
+
`);return{platform:r[0],arch:r[1]}}async getUserHomeDirectory(){const{stdout:t,stderr:e}=await this.sshClient.exec("echo $HOME");if(e){const r=`/home/${this.username}`;return console.warn(`getUserHomeDirectory: stderr is not empty: ${e}, stdout: ${t}, will get a default home: ${r}`),r}return t.trim()}}const Qt={useGlobalAccess:!1,plBinary:{type:"Download",version:A()}};async function te(n,t){try{const{stdout:e,stderr:r}=await t.exec("ldd --version | head -n 1");if(r)throw new Error(`Failed to check glibc version: ${r}`);return st(e)}catch(e){throw n.error(`glibc version check failed: ${e}`),e}}function st(n){const t=n.match(/\d+\.\d+/);if(!t)throw new Error(`Could not parse glibc version from: ${n}`);return parseFloat(t[0])}exports.ConnectionInfo=rt;exports.LocalConfigYaml=V;exports.LocalPl=W;exports.PortPair=$;exports.SshClient=O;exports.SshPl=B;exports.SshPlPorts=et;exports.getDefaultPlVersion=A;exports.localPlatformaInit=Nt;exports.mergeDefaultOps=K;exports.newConnectionInfo=it;exports.parseConnectionInfo=nt;exports.parseGlibcVersion=st;exports.plProcessOps=Y;exports.stringifyConnectionInfo=ot;
|
|
61
65
|
//# sourceMappingURL=index.js.map
|