@constellation-network/node-pilot 0.0.13 → 0.0.15

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 CHANGED
@@ -21,7 +21,7 @@ $ npm install -g @constellation-network/node-pilot
21
21
  $ cpilot COMMAND
22
22
  running command...
23
23
  $ cpilot (--version|-v)
24
- @constellation-network/node-pilot/0.0.13 darwin-arm64 node-v22.15.0
24
+ @constellation-network/node-pilot/0.0.15 darwin-arm64 node-v24.8.0
25
25
  $ cpilot --help [COMMAND]
26
26
  USAGE
27
27
  $ cpilot COMMAND
@@ -62,7 +62,7 @@ EXAMPLES
62
62
  $ cpilot config
63
63
  ```
64
64
 
65
- _See code: [src/commands/config.ts](https://github.com/Constellation-Labs/node-pilot/blob/v0.0.13/src/commands/config.ts)_
65
+ _See code: [src/commands/config.ts](https://github.com/Constellation-Labs/node-pilot/blob/v0.0.15/src/commands/config.ts)_
66
66
 
67
67
  ## `cpilot config get [NAME]`
68
68
 
@@ -86,7 +86,7 @@ EXAMPLES
86
86
  $ cpilot config get gl0:CL_PUBLIC_HTTP_PORT
87
87
  ```
88
88
 
89
- _See code: [src/commands/config/get.ts](https://github.com/Constellation-Labs/node-pilot/blob/v0.0.13/src/commands/config/get.ts)_
89
+ _See code: [src/commands/config/get.ts](https://github.com/Constellation-Labs/node-pilot/blob/v0.0.15/src/commands/config/get.ts)_
90
90
 
91
91
  ## `cpilot config set NAME VALUE`
92
92
 
@@ -109,7 +109,7 @@ EXAMPLES
109
109
  $ cpilot config set gl0:CL_PUBLIC_HTTP_PORT 9000
110
110
  ```
111
111
 
112
- _See code: [src/commands/config/set.ts](https://github.com/Constellation-Labs/node-pilot/blob/v0.0.13/src/commands/config/set.ts)_
112
+ _See code: [src/commands/config/set.ts](https://github.com/Constellation-Labs/node-pilot/blob/v0.0.15/src/commands/config/set.ts)_
113
113
 
114
114
  ## `cpilot help [COMMAND]`
115
115
 
@@ -146,7 +146,7 @@ EXAMPLES
146
146
  $ cpilot info
147
147
  ```
148
148
 
149
- _See code: [src/commands/info.ts](https://github.com/Constellation-Labs/node-pilot/blob/v0.0.13/src/commands/info.ts)_
149
+ _See code: [src/commands/info.ts](https://github.com/Constellation-Labs/node-pilot/blob/v0.0.15/src/commands/info.ts)_
150
150
 
151
151
  ## `cpilot logs LAYER`
152
152
 
@@ -170,7 +170,7 @@ EXAMPLES
170
170
  $ cpilot logs
171
171
  ```
172
172
 
173
- _See code: [src/commands/logs.ts](https://github.com/Constellation-Labs/node-pilot/blob/v0.0.13/src/commands/logs.ts)_
173
+ _See code: [src/commands/logs.ts](https://github.com/Constellation-Labs/node-pilot/blob/v0.0.15/src/commands/logs.ts)_
174
174
 
175
175
  ## `cpilot restart [LAYER]`
176
176
 
@@ -198,7 +198,7 @@ EXAMPLES
198
198
  $ cpilot restart
199
199
  ```
200
200
 
201
- _See code: [src/commands/restart.ts](https://github.com/Constellation-Labs/node-pilot/blob/v0.0.13/src/commands/restart.ts)_
201
+ _See code: [src/commands/restart.ts](https://github.com/Constellation-Labs/node-pilot/blob/v0.0.15/src/commands/restart.ts)_
202
202
 
203
203
  ## `cpilot shutdown`
204
204
 
@@ -215,7 +215,7 @@ EXAMPLES
215
215
  $ cpilot shutdown
216
216
  ```
217
217
 
218
- _See code: [src/commands/shutdown.ts](https://github.com/Constellation-Labs/node-pilot/blob/v0.0.13/src/commands/shutdown.ts)_
218
+ _See code: [src/commands/shutdown.ts](https://github.com/Constellation-Labs/node-pilot/blob/v0.0.15/src/commands/shutdown.ts)_
219
219
 
220
220
  ## `cpilot status`
221
221
 
@@ -229,5 +229,5 @@ DESCRIPTION
229
229
  Display node status and configuration settings
230
230
  ```
231
231
 
232
- _See code: [src/commands/status.ts](https://github.com/Constellation-Labs/node-pilot/blob/v0.0.13/src/commands/status.ts)_
232
+ _See code: [src/commands/status.ts](https://github.com/Constellation-Labs/node-pilot/blob/v0.0.15/src/commands/status.ts)_
233
233
  <!-- commandsstop -->
@@ -2,6 +2,7 @@ import { input } from "@inquirer/prompts";
2
2
  import chalk from "chalk";
3
3
  import { clm } from "../clm.js";
4
4
  import { configStore } from "../config-store.js";
5
+ import { StatusTable } from "../helpers/status-table.js";
5
6
  import { dockerService } from "../services/docker-service.js";
6
7
  import { nodeService } from "../services/node-service.js";
7
8
  export const checkLayers = {
@@ -62,17 +63,7 @@ export const checkLayers = {
62
63
  }
63
64
  },
64
65
  async layersStatus() {
65
- const { layersToRun } = configStore.getProjectInfo();
66
- const promises = layersToRun.map(l => nodeService.getNodeInfo(l));
67
- const results = await Promise.all(promises);
68
- const states = results.map(r => r.state);
69
- clm.preStep("-----------------------");
70
- clm.preStep(" VALIDATOR NODE STATUS ");
71
- clm.preStep("-----------------------");
72
- for (const [i, layer] of layersToRun.entries()) {
73
- const state = states[i];
74
- clm.step(` "${layer.toUpperCase()}" is "${state}"`);
75
- }
66
+ await StatusTable.run();
76
67
  },
77
68
  async nodeReadyToJoin(layer) {
78
69
  const status = await nodeService.getNodeInfo(layer);
@@ -1,4 +1,5 @@
1
1
  export declare const checkNetwork: {
2
+ checkExternalIpAddress(): Promise<void> | undefined;
2
3
  checkForExistingNodeIdInCluster(): Promise<void>;
3
4
  checkSeedList(): Promise<void>;
4
5
  configureIpAddress(): Promise<void>;
@@ -8,6 +8,12 @@ import { clusterService } from "../services/cluster-service.js";
8
8
  import { dockerService } from "../services/docker-service.js";
9
9
  import { shellService } from "../services/shell-service.js";
10
10
  export const checkNetwork = {
11
+ checkExternalIpAddress() {
12
+ const { CL_EXTERNAL_IP } = configStore.getEnvInfo();
13
+ if (!CL_EXTERNAL_IP) {
14
+ return this.configureIpAddress();
15
+ }
16
+ },
11
17
  async checkForExistingNodeIdInCluster() {
12
18
  if (configStore.hasProjectFlag('duplicateNodeIdChecked')) {
13
19
  return;
@@ -1,6 +1,4 @@
1
1
  import { input, select } from "@inquirer/prompts";
2
- import chalk from "chalk";
3
- import ora from 'ora';
4
2
  import { clm } from "../clm.js";
5
3
  import { configStore } from "../config-store.js";
6
4
  import { configHelper } from "../helpers/config-helper.js";
@@ -96,29 +94,31 @@ export const checkProject = {
96
94
  if (isRunning) {
97
95
  await dockerService.dockerDown();
98
96
  }
99
- const silent = !process.env.DEBUG;
100
- const spinner = ora('Running install script...');
101
- if (silent) {
102
- spinner.start();
103
- spinner.color = 'green';
104
- }
105
- else {
106
- clm.preStep('Running install script...');
107
- }
97
+ // const silent = !process.env.DEBUG;
98
+ //
99
+ // const spinner = ora('');
100
+ //
101
+ // if (silent) {
102
+ // spinner.start();
103
+ // spinner.color = 'green';
104
+ // }
105
+ // else {
106
+ clm.preStep('Running install script...');
107
+ // }
108
108
  // const node = await clusterService.getClusterNodeInfo();
109
109
  // const NODE_URL = `http://${node.host}:${node.publicPort}`;
110
110
  // NOTE: may be different for metagraphs
111
- await shellService.runProjectCommand(`scripts/install.sh ${nInfo.type}`, undefined, silent)
111
+ await shellService.runProjectCommand(`scripts/install.sh ${nInfo.type}`, undefined, false)
112
112
  .catch(() => {
113
- spinner.stop();
114
- if (silent) {
115
- clm.error(`Install script failed. run: ${chalk.cyan("DEBUG=true cpilot")} for more details`);
116
- }
113
+ // spinner.stop();
114
+ // if (silent) {
115
+ // clm.error(`Install script failed. run: ${chalk.cyan("DEBUG=true cpilot")} for more details`);
116
+ // }
117
117
  clm.error('Install script failed. Please run cpilot again after correcting the error');
118
118
  });
119
- if (silent) {
120
- spinner.stop();
121
- }
119
+ // if (silent) {
120
+ // spinner.stop();
121
+ // }
122
122
  rInfo = await configHelper.getReleaseInfo();
123
123
  configStore.setNetworkInfo({
124
124
  type: rInfo.network,
@@ -20,7 +20,7 @@ export default class Info extends Command {
20
20
  // Node ID
21
21
  configHelper.showEnvInfo('Node ID', projectInfo.nodeId);
22
22
  // Layers to Run
23
- configHelper.showEnvInfo('Layers to Run', projectInfo.layersToRun.join(', '));
23
+ configHelper.showEnvInfo('Layers to Run', projectInfo.layersToRun?.join(', '));
24
24
  // Network type
25
25
  configHelper.showEnvInfo('Network', networkInfo.type);
26
26
  // Network version
@@ -27,7 +27,7 @@ export default class Restart extends BaseCommand {
27
27
  serviceLog.log('Executing "cpilot restart --update" at ' + new Date().toLocaleString('en-US', { timeZone: 'America/Los_Angeles' }));
28
28
  const project = configStore.getActiveProject();
29
29
  const activeProjects = configStore.getRunningProjects();
30
- serviceLog.log(` Active projects: ${activeProjects.join(', ')}...`);
30
+ // serviceLog.log(` Active projects: ${activeProjects.join(', ')}...`);
31
31
  for (const project of activeProjects) {
32
32
  configStore.setActiveProject(project);
33
33
  // eslint-disable-next-line no-await-in-loop
@@ -44,7 +44,7 @@ export default class Restart extends BaseCommand {
44
44
  serviceLog.log('Executing "cpilot restart --autostart" at ' + new Date().toLocaleString('en-US', { timeZone: 'America/Los_Angeles' }));
45
45
  const project = configStore.getActiveProject();
46
46
  const activeProjects = configStore.getRunningProjects();
47
- serviceLog.log(` Active projects: ${activeProjects.join(', ')}...`);
47
+ // serviceLog.log(` Active projects: ${activeProjects.join(', ')}...`);
48
48
  for (const project of activeProjects) {
49
49
  serviceLog.log(' ' + project + ' is restarting...');
50
50
  configStore.setActiveProject(project);
@@ -75,16 +75,24 @@ export default class Restart extends BaseCommand {
75
75
  // const pAll = layersToRun.map(l => nodeService.getNodeInfo(l));
76
76
  // const info = await Promise.all(pAll);
77
77
  // const isRunning = info.some(n => n.state !== 'Unavailable');
78
- if (await dockerService.isRunning()) {
79
- await nodeService.leaveClusterAllLayers();
80
- const { layersToRun } = configStore.getProjectInfo();
81
- await nodeService.pollForLayersState(layersToRun, 'Offline');
82
- clm.preStep('Stopping the node...');
83
- await dockerService.dockerDown();
78
+ if (configStore.isRestarting())
79
+ return;
80
+ configStore.setIsRestarting(true);
81
+ try {
82
+ if (await dockerService.isRunning()) {
83
+ await nodeService.leaveClusterAllLayers();
84
+ const { layersToRun } = configStore.getProjectInfo();
85
+ await nodeService.pollForLayersState(layersToRun, 'Offline');
86
+ clm.preStep('Stopping the node...');
87
+ await dockerService.dockerDown();
88
+ }
89
+ clm.preStep('Checking for a new version...');
90
+ await checkProject.runUpgrade();
91
+ clm.preStep('Starting the node...');
92
+ await dockerService.dockerUp();
93
+ }
94
+ finally {
95
+ configStore.setIsRestarting(false);
84
96
  }
85
- clm.preStep('Checking for a new version...');
86
- await checkProject.runUpgrade();
87
- clm.preStep('Starting the node...');
88
- await dockerService.dockerUp();
89
97
  }
90
98
  }
@@ -17,6 +17,7 @@ export default class Status extends Command {
17
17
  export async function checkInstallationAndConfigurationStatus() {
18
18
  await checkInitialSetup.firstTimeRun();
19
19
  await checkProject.projectInstallation();
20
+ await checkNetwork.checkExternalIpAddress();
20
21
  await checkNetwork.isNetworkConnectable();
21
22
  await checkProject.releaseVersion();
22
23
  await checkNodeCtl.check4Migration();
@@ -4,6 +4,7 @@ import fs from "node:fs";
4
4
  import path from "node:path";
5
5
  import { fileURLToPath } from "node:url";
6
6
  import { getRandomNode } from "../services/get-random-node.js";
7
+ import { StatusTable } from "../helpers/status-table.js";
7
8
  export default class Test extends Command {
8
9
  static description = 'node pilot test command';
9
10
  static hidden = true;
@@ -17,6 +18,7 @@ export default class Test extends Command {
17
18
  //
18
19
  // await archiverService.syncToLatestSnapshot();
19
20
  // await archiverService.checkLogsForMissingSnapshots();
21
+ await StatusTable.run();
20
22
  }
21
23
  async testRandomNode() {
22
24
  const filePath = path.resolve(path.dirname(fileURLToPath(import.meta.url)), `../../projects/hypergraph/networks/mainnet/source-nodes.env`);
@@ -18,6 +18,7 @@ declare class ConfigStore {
18
18
  getSystemInfo(): SystemInfo;
19
19
  hasProjectFlag(name: string): any;
20
20
  hasProjects(): boolean;
21
+ isRestarting(): boolean;
21
22
  setActiveProject(name: string): void;
22
23
  setClusterStats(info: Partial<ClusterStats>): void;
23
24
  setDockerEnvInfo(info: Partial<{
@@ -27,6 +28,7 @@ declare class ConfigStore {
27
28
  setEnvInfo(info: Partial<EnvInfo>): void;
28
29
  setEnvLayerInfo(network: NetworkType, layer: TessellationLayer, info: Partial<EnvLayerInfo>): void;
29
30
  setEnvNetworkInfo(network: NetworkType, info: Partial<EnvNetworkInfo>): void;
31
+ setIsRestarting(val: boolean): void;
30
32
  setNetworkInfo(info: Partial<NetworkInfo>): void;
31
33
  setProjectFlag(name: string, value: boolean): any;
32
34
  setProjectInfo(info: Partial<ProjectInfo>): void;
@@ -20,7 +20,7 @@ class ConfigStore {
20
20
  this.pilotStore = new JSONStorage(path.join(appDir, 'config'));
21
21
  const appInfo = this.pilotStore.getItem('pilot');
22
22
  if (!appInfo) {
23
- this.pilotStore.setItem('pilot', { appDir, project: 'undefined', projects: [], running: [] });
23
+ this.pilotStore.setItem('pilot', { appDir, project: 'undefined', projects: [], restarting: false, running: [] });
24
24
  }
25
25
  const { project } = this.pilotStore.getItem('pilot');
26
26
  this.projectStore = project === 'undefined' ? new EmptyStorage() : new JSONStorage(path.join(appDir, project, 'config'));
@@ -104,6 +104,10 @@ class ConfigStore {
104
104
  const { projects } = this.pilotStore.getItem('pilot');
105
105
  return projects.length > 0;
106
106
  }
107
+ isRestarting() {
108
+ const { restarting } = this.pilotStore.getItem('pilot');
109
+ return restarting;
110
+ }
107
111
  setActiveProject(name) {
108
112
  const { appDir, project, projects } = this.pilotStore.getItem('pilot');
109
113
  if (projects && projects.includes(name)) {
@@ -142,6 +146,9 @@ class ConfigStore {
142
146
  networks = {};
143
147
  this.projectStore.setItem('network-env', { ...networks, [network]: { ...networks[network], ...info } });
144
148
  }
149
+ setIsRestarting(val) {
150
+ this.setPilotInfo({ restarting: val });
151
+ }
145
152
  setNetworkInfo(info) {
146
153
  const oldInfo = this.projectStore.getItem('network');
147
154
  this.projectStore.setItem('network', { ...oldInfo, ...info });
@@ -6,6 +6,7 @@ import { fileURLToPath } from 'node:url';
6
6
  import { clm } from "../clm.js";
7
7
  import { configStore } from "../config-store.js";
8
8
  import { githubService } from "../services/github-service.js";
9
+ import { shellService } from "../services/shell-service.js";
9
10
  import { configHelper } from "./config-helper.js";
10
11
  import { getLayerEnvFileContent } from "./env-templates.js";
11
12
  export const projectHelper = {
@@ -63,11 +64,19 @@ export const projectHelper = {
63
64
  async installHypergraph() {
64
65
  await this.installEmbedded('hypergraph');
65
66
  const { projectDir } = configStore.getProjectInfo();
66
- // Create gl0 folders for fast forward feature before Docker does
67
- const gl0DataDir = path.join(projectDir, 'gl0', 'data');
68
- fs.mkdirSync(path.join(gl0DataDir, 'incremental_snapshot'), { recursive: true });
69
- fs.mkdirSync(path.join(gl0DataDir, 'snapshot_info'));
70
- fs.mkdirSync(path.join(gl0DataDir, 'tmp'));
67
+ const { platform } = configStore.getSystemInfo();
68
+ // Create gl0 folder for the fast-forward feature before Docker does
69
+ if (platform === 'linux') {
70
+ const layerDir = path.join(projectDir, 'gl0');
71
+ // set permission for group "docker" on the layer folder and any subfolders created later
72
+ await shellService.runCommand(`sudo setfacl -m g:docker:rwX -dm g:docker:rwX ${layerDir}`);
73
+ }
74
+ else {
75
+ const gl0DataDir = path.join(projectDir, 'gl0', 'data');
76
+ fs.mkdirSync(path.join(gl0DataDir, 'incremental_snapshot'), { recursive: true });
77
+ fs.mkdirSync(path.join(gl0DataDir, 'snapshot_info'));
78
+ fs.mkdirSync(path.join(gl0DataDir, 'tmp'));
79
+ }
71
80
  this.importEnvFiles();
72
81
  },
73
82
  async installProject(name, projectFolder) {
@@ -0,0 +1,29 @@
1
+ export declare const statusTableHeader: ({
2
+ color: string;
3
+ headerColor: string;
4
+ value: string;
5
+ formatter?: undefined;
6
+ width?: undefined;
7
+ } | {
8
+ color: string;
9
+ formatter: (startTime: number | string) => string;
10
+ headerColor: string;
11
+ value: string;
12
+ width?: undefined;
13
+ } | {
14
+ color: string;
15
+ formatter: (value: string) => string;
16
+ headerColor: string;
17
+ value: string;
18
+ width: number;
19
+ })[];
20
+ export type NodeStatusInfo = {
21
+ clusterOrdinal: number;
22
+ clusterSession: string;
23
+ clusterState: string;
24
+ error: string;
25
+ ordinal: number;
26
+ pilotSession: string;
27
+ session: string;
28
+ state: string;
29
+ };
@@ -0,0 +1,62 @@
1
+ class CellFormatter {
2
+ formatDistance(value) {
3
+ if (value === '-')
4
+ return value;
5
+ const num = Number(value);
6
+ if (num === 0)
7
+ return value;
8
+ if (num < 4)
9
+ return this.style(value.toString(), "bgYellow", "bold");
10
+ return this.style(value.toString(), "bgRed", "bold");
11
+ }
12
+ formatError(value) {
13
+ if (value === '-')
14
+ return value;
15
+ return this.style(value, "bgRed", "bold");
16
+ }
17
+ formatOrdinal(value) {
18
+ const [v, changed] = value.split(':');
19
+ if (changed) {
20
+ return this.style(v, "bgCyan");
21
+ }
22
+ return this.style(v, "cyan");
23
+ }
24
+ formatState(value) {
25
+ if (value === 'Offline')
26
+ return this.style(value, "bgRed", "bold");
27
+ if (value === 'Ready')
28
+ return this.style(value, "green");
29
+ if (value === 'ReadyToJoin')
30
+ return this.style(value, "yellow", "bold");
31
+ if (value === 'Restarting')
32
+ return this.style(value, "yellow", "bold");
33
+ return this.style(value, "white");
34
+ }
35
+ formatUpTIme(startTime) {
36
+ if (!startTime)
37
+ return '-';
38
+ const upTimeMs = Date.now() - Number(startTime);
39
+ const upTimeSec = Math.floor(upTimeMs / 1000);
40
+ const hours = Math.floor(upTimeSec / 3600);
41
+ const minutes = Math.floor((upTimeSec % 3600) / 60);
42
+ const seconds = upTimeSec % 60;
43
+ if (hours < 1 && minutes < 1)
44
+ return `${seconds}s`;
45
+ return hours > 0 ? `${hours}h ${minutes}m ${seconds}s` : `${minutes}m ${seconds}s`;
46
+ }
47
+ style(value, color, style) {
48
+ return `c${color}v${value}s${style}`;
49
+ }
50
+ }
51
+ const { formatDistance, formatError, formatOrdinal, formatState, formatUpTIme } = new CellFormatter();
52
+ // Layer | Uptime | State | Ordinal | Distance from cluster | Cluster State | Error
53
+ export const statusTableHeader = [
54
+ { color: 'white', headerColor: 'whiteBright', value: 'Network' },
55
+ { color: 'whiteBright', headerColor: 'whiteBright', value: 'Layer' },
56
+ { color: 'white', formatter: formatUpTIme, headerColor: 'whiteBright', value: 'Uptime' },
57
+ { color: 'white', formatter: formatState, headerColor: 'whiteBright', value: 'Node State', width: 18 },
58
+ { color: 'white', formatter: formatOrdinal, headerColor: 'whiteBright', value: 'Ordinal' },
59
+ { color: 'white', formatter: formatDistance, headerColor: 'whiteBright', value: 'Ord. lag' },
60
+ { color: 'white', formatter: formatState, headerColor: 'whiteBright', value: 'Cluster State', width: 16 },
61
+ { color: 'white', formatter: formatError, headerColor: 'whiteBright', value: 'Error', width: 22 },
62
+ ];
@@ -0,0 +1,8 @@
1
+ export declare class StatusTable {
2
+ private alreadyRendered;
3
+ private previousHeight;
4
+ static run(): Promise<void>;
5
+ private getProjectInfo;
6
+ private monitorState;
7
+ private render;
8
+ }
@@ -0,0 +1,129 @@
1
+ import { createPrompt, useKeypress } from '@inquirer/core';
2
+ import fs from "node:fs";
3
+ import os from "node:os";
4
+ import ttyTable from "tty-table";
5
+ import { clm } from "../clm.js";
6
+ import { configStore } from "../config-store.js";
7
+ import { statusTableHeader } from "./status-table-helper.js";
8
+ // mismatched snapshot hash, cluster fork, lagging behind cluster, stalled state, missing snapshot
9
+ // const possibleErrors = [
10
+ // '-',
11
+ // 'Bad snapshot hash',
12
+ // 'Cluster fork',
13
+ // 'Lagging behind',
14
+ // 'Stalled state',
15
+ // 'Missing snapshot',
16
+ // ]
17
+ //
18
+ // const possibleStates =
19
+ // [
20
+ // 'Ready',
21
+ // 'Restarting',
22
+ // 'Offline',
23
+ // 'SessionStarted'
24
+ // ]
25
+ export class StatusTable {
26
+ alreadyRendered = false;
27
+ previousHeight = 0;
28
+ static async run() {
29
+ process.on("exit", () => {
30
+ // show cursor
31
+ console.log("\u001B[?25h");
32
+ });
33
+ process.on("SIGINT", () => {
34
+ process.exit();
35
+ });
36
+ const table = new StatusTable();
37
+ table.monitorState();
38
+ const onKeyPress = createPrompt((_, done) => {
39
+ useKeypress(() => { done(''); });
40
+ return '';
41
+ });
42
+ await onKeyPress({});
43
+ console.log("\u001B[?25h");
44
+ process.exit();
45
+ }
46
+ getProjectInfo() {
47
+ const appPath = os.homedir() + '/.node-pilot';
48
+ const activeProject = configStore.getActiveProject();
49
+ const projects = configStore.getRunningProjects();
50
+ if (!activeProject || projects.length === 0) {
51
+ clm.error("No running projects found.");
52
+ }
53
+ const info = [];
54
+ for (const project of projects) {
55
+ configStore.setActiveProject(project);
56
+ const { layersToRun } = configStore.getProjectInfo();
57
+ const { type: network } = configStore.getNetworkInfo();
58
+ for (const layer of layersToRun) {
59
+ info.push({ layer, network, path: `${appPath}/${project}/${layer}/data/health-check/node`, project });
60
+ }
61
+ }
62
+ configStore.setActiveProject(activeProject);
63
+ return info;
64
+ }
65
+ // Layer | Uptime | State | Ordinal | Distance from cluster | Cluster State | Error
66
+ // state: Restarting...
67
+ // cluster state: Ready, Offline, Restarting...
68
+ // possibleErrors: mismatched snapshot hash, cluster fork, lagging behind cluster, stalled state, missing snapshot
69
+ async monitorState() {
70
+ const projects = this.getProjectInfo();
71
+ const values = {};
72
+ while (true) {
73
+ const rows = [];
74
+ for (const p of projects) {
75
+ if (!values[p.layer])
76
+ values[p.layer] = {};
77
+ const n = JSON.parse(fs.readFileSync(p.path, 'utf8'));
78
+ const projectName = p.project === 'hypergraph' ? '' : p.project;
79
+ const network = p.network === 'integrationnet' ? 'intnet' : p.network;
80
+ const distance = Number.isNaN(n.clusterOrdinal - n.ordinal) ? '-' : String(n.clusterOrdinal - n.ordinal);
81
+ const ordinal = (values[p.layer].ordinal === n.ordinal) ? String(n.ordinal) : n.ordinal + ':true';
82
+ rows.push([
83
+ projectName + ':' + network,
84
+ p.layer,
85
+ n.session, // n.pilotSession,
86
+ n.state,
87
+ n.ordinal ? ordinal : '-',
88
+ distance,
89
+ n.clusterState || 'Ready',
90
+ n.error || '-'
91
+ ]);
92
+ values[p.layer].ordinal = n.ordinal;
93
+ }
94
+ // const rows = [
95
+ // ["GL0", '1760103674654', possibleStates[Math.floor(Math.random()*possibleStates.length)], '5174762', String(Math.floor(Math.random()*8)), possibleStates[Math.floor(Math.random()*possibleStates.length)], possibleErrors[Math.floor(Math.random()*possibleErrors.length)]],
96
+ // ["GL1", '1760102674654', possibleStates[Math.floor(Math.random()*possibleStates.length)], '4174762', String(Math.floor(Math.random()*8)), possibleStates[Math.floor(Math.random()*possibleStates.length)], possibleErrors[Math.floor(Math.random()*possibleErrors.length)]],
97
+ // ]
98
+ this.render(rows);
99
+ process.stdout.write(" *press any key to cancel");
100
+ // eslint-disable-next-line no-await-in-loop
101
+ await sleep(1);
102
+ }
103
+ }
104
+ render(body) {
105
+ const options = { terminalAdapter: true };
106
+ const t1 = ttyTable(statusTableHeader, body, options);
107
+ // hide cursor
108
+ console.log("\u001B[?25l");
109
+ // wipe existing if already rendered
110
+ if (this.alreadyRendered) {
111
+ // move cursor up number to the top of the previous print before deleting
112
+ console.log(`\u001B[${this.previousHeight + 3}A`);
113
+ // delete to end of terminal
114
+ console.log("\u001B[0J");
115
+ }
116
+ else {
117
+ this.alreadyRendered = true;
118
+ }
119
+ console.log(t1.render());
120
+ // reset the previous height to the height of this output
121
+ // for when we next clear the print
122
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
123
+ // @ts-expect-error
124
+ this.previousHeight = t1.height;
125
+ }
126
+ }
127
+ function sleep(sec) {
128
+ return new Promise(resolve => { setTimeout(resolve, sec * 1000); });
129
+ }
@@ -5,6 +5,7 @@ import 'json-bigint-patch';
5
5
  import path from "node:path";
6
6
  import { clm } from "../clm.js";
7
7
  import { configStore } from "../config-store.js";
8
+ import { promptHelper } from "../helpers/prompt-helper.js";
8
9
  const CHUNK_SIZE = 20_000;
9
10
  export class FastforwardService {
10
11
  dataDir;
@@ -19,17 +20,19 @@ export class FastforwardService {
19
20
  this.dataDir = path.join(projectDir, 'gl0', 'data');
20
21
  fs.mkdirSync(this.tmpDir, { recursive: true });
21
22
  fs.mkdirSync(this.dataDir, { recursive: true });
22
- const env = configStore.getEnvNetworkInfo(type);
23
- // this.lbUrl = `https://l0-lb-${this.network}.constellationnetwork.io`;
24
- this.lbUrl = `http://${env.CL_L0_PEER_HTTP_HOST}:${env.CL_L0_PEER_HTTP_PORT}`;
23
+ // const env = configStore.getEnvNetworkInfo(type);
24
+ this.lbUrl = `https://l0-lb-${this.network}.constellationnetwork.io`;
25
+ // this.lbUrl = `http://${env.CL_L0_PEER_HTTP_HOST}:${env.CL_L0_PEER_HTTP_PORT}`;
25
26
  }
26
27
  static async synctoLatestSnapshot() {
27
28
  const ffs = new FastforwardService();
28
- await ffs.runFastForwardSnapshot().catch(() => {
29
+ await ffs.runFastForwardSnapshot().catch(async (error) => {
30
+ clm.debug(error);
29
31
  const { projectDir } = configStore.getProjectInfo();
30
32
  const dataDir = path.join(projectDir, 'gl0', 'data', 'incremental_snapshot', 'ordinal');
31
33
  if (fs.existsSync(dataDir) && fs.readdirSync(dataDir).length > 0) {
32
34
  clm.warn('Failed to fast forward to latest snapshot. Skipping...');
35
+ await promptHelper.doYouWishToContinue();
33
36
  return;
34
37
  }
35
38
  clm.error('Failed to fast forward to latest snapshot. Please try again later.');
@@ -43,7 +46,10 @@ export class FastforwardService {
43
46
  // clm.debug('hash', Buffer.from(jsSha256.sha256(compressedSnapshotIncremental)).toString('hex'));
44
47
  const snapshotInfoDir = path.join(this.dataDir, 'snapshot_info');
45
48
  fs.mkdirSync(snapshotInfoDir, { recursive: true });
46
- fs.writeFileSync(path.join(snapshotInfoDir, ordinal.toString()), compressedSnapshotInfo);
49
+ const ordinalDir = path.join(snapshotInfoDir, ordinal.toString());
50
+ clm.debug('Saving compressedSnapshotInfo to: ' + ordinalDir);
51
+ fs.writeFileSync(ordinalDir, compressedSnapshotInfo);
52
+ clm.debug('Saving compressedSnapshotIncremental to: ' + this.tmpDir);
47
53
  fs.writeFileSync(path.join(this.tmpDir, ordinal.toString() + '.c'), compressedSnapshotIncremental);
48
54
  await this.saveSnapshotFiles(ordinal.toString(), hash);
49
55
  clm.postStep(`Fastforward to snapshot "${ordinal}" completed.`);
@@ -94,9 +100,12 @@ export class FastforwardService {
94
100
  clm.warn(`Snapshot ${destOrdinalFile} already exists. Skipping...`);
95
101
  return;
96
102
  }
103
+ clm.debug(`Saving snapshot ${ordinal} to ${destOrdinalFile}`);
97
104
  fs.copyFileSync(ordinalFile, destOrdinalFile);
98
105
  const hashFile = path.join(hashDir, hash);
106
+ clm.debug(`Saving hashFile to ${hashFile}`);
99
107
  fs.linkSync(destOrdinalFile, hashFile);
108
+ clm.debug(`Saving snapshot version to ${path.join(this.dataDir, 'snapshot-version')}`);
100
109
  fs.writeFileSync(path.join(this.dataDir, 'snapshot-version'), `${this.network}:${ordinal}`);
101
110
  }
102
111
  }
@@ -13,6 +13,7 @@ export const nodeService = {
13
13
  .then(res => {
14
14
  if (res.ok)
15
15
  return res.json().then(i => ({ ...i, layer }));
16
+ clm.warn(`Failed to get node info from ${res.url} (${res.status})`);
16
17
  throw new Error(`Failed`);
17
18
  })
18
19
  .catch(() => ({ layer, state: "Unavailable" }));
@@ -62,7 +63,7 @@ export const nodeService = {
62
63
  const url = `http://localhost:${cliPort}/cluster/join`;
63
64
  clm.preStep(`Joining ${layer} to cluster`); // on ${cliPort}...${body} ... ${url}`);
64
65
  await shellService.execDockerShell(layer, `curl -X POST '${url}' -H 'Content-Type: application/json' --data '${body}'`);
65
- await this.pollForState(layer, 'Ready');
66
+ // await this.pollForState(layer, 'Ready');
66
67
  },
67
68
  async leaveCluster(layer) {
68
69
  const { state } = await this.getNodeInfo(layer);
@@ -103,7 +104,7 @@ export const nodeService = {
103
104
  async pollForState(layer, expectedState = 'ReadyToJoin') {
104
105
  clm.preStep(`Waiting for ${layer.toUpperCase()} Validator Node to become ${expectedState}...`);
105
106
  await sleep(1);
106
- for (let i = 1; i <= 60; i++) {
107
+ for (let i = 1; i <= 24; i++) {
107
108
  // eslint-disable-next-line no-await-in-loop
108
109
  const { state } = await this.getNodeInfo(layer);
109
110
  if (expectedState === 'Offline' && (state === "Unavailable" || state === "ReadyToJoin" || state === "SessionStarted"))
@@ -123,7 +124,7 @@ export const nodeService = {
123
124
  // eslint-disable-next-line no-await-in-loop
124
125
  await sleep(5);
125
126
  }
126
- clm.warn(`${layer} is not ${expectedState} after 5 minutes`);
127
+ clm.warn(`${layer} is not ${expectedState} after 2 minutes`);
127
128
  return false;
128
129
  }
129
130
  };
package/dist/types.d.ts CHANGED
@@ -38,14 +38,6 @@ export type ClusterStats = {
38
38
  startTime: string;
39
39
  total: number;
40
40
  };
41
- export type NodeStatusInfo = {
42
- clusterSession: string;
43
- id: string;
44
- inConsensus: boolean;
45
- inNetwork: boolean;
46
- ip: string;
47
- session: string;
48
- };
49
41
  export type NodeDiagnosticInfo = {
50
42
  collateral: number;
51
43
  hasCollateral: boolean;
@@ -3,6 +3,32 @@
3
3
  # Break on any error
4
4
  set -e
5
5
 
6
+ check_acl() {
7
+ # echo "Checking if ACL is enabled on filesystem"
8
+ if [[ "$(getfacl . | grep 'mask:')" != "" ]]; then
9
+ echo "✅ ACL is enabled on filesystem."
10
+ return 0
11
+ else
12
+ echo "⚠️ ACL is not enabled on filesystem. Attempting to enable it now."
13
+ if [[ "$(uname)" == "Linux" ]]; then
14
+ if command -v apt >/dev/null 2>&1; then
15
+ echo "Enabling ACL using apt..."
16
+ sudo apt install -y acl
17
+ else
18
+ echo "⚠️ Unsupported Linux distribution. Please enable ACL manually."
19
+ return 1
20
+ fi
21
+ echo "✅ ACL installation complete. "
22
+ elif [[ "$(uname)" == "Darwin" ]]; then
23
+ echo "⚠️ macOS does not support enabling ACL via this script. Please ensure ACL is enabled manually."
24
+ return 1
25
+ else
26
+ echo "⚠️ Unsupported OS: $(uname). Please enable ACL manually."
27
+ return 1
28
+ fi
29
+ fi
30
+ }
31
+
6
32
  # Check and install curl
7
33
  check_curl() {
8
34
  # echo "Checking for curl..."
@@ -62,16 +88,10 @@ check_docker() {
62
88
 
63
89
  case "$(uname)" in
64
90
  Linux)
65
- if command -v apt >/dev/null 2>&1; then
66
- echo "Installing Docker using script..."
67
- curl -fsSL https://get.docker.com -o get-docker.sh
68
- sudo sh ./get-docker.sh
69
- sudo usermod -aG docker $USER
70
- echo "Docker installed. You may need to log out and back in for group changes to take effect."
71
- else
72
- echo "⚠️ Unsupported Linux distribution. Please install Docker manually."
73
- return 1
74
- fi
91
+ echo "Installing Docker using script..."
92
+ curl -fsSL https://get.docker.com | sudo sh
93
+ sudo usermod -aG docker $USER
94
+ echo "Docker installed. You NEED to log out and log back in for the changes to take effect."
75
95
  ;;
76
96
  Darwin)
77
97
  echo "Please install Docker Desktop manually from https://www.docker.com/products/docker-desktop"
@@ -94,5 +114,6 @@ check_docker() {
94
114
 
95
115
  # Run all checks
96
116
  echo "🔍 Checking and installing required dependencies..."
117
+ check_acl
97
118
  check_curl
98
119
  check_docker
@@ -276,5 +276,5 @@
276
276
  ]
277
277
  }
278
278
  },
279
- "version": "0.0.13"
279
+ "version": "0.0.15"
280
280
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@constellation-network/node-pilot",
3
3
  "description": "An easy deployment and monitoring tool for Constellation nodes.",
4
- "version": "0.0.13",
4
+ "version": "0.0.15",
5
5
  "author": "Frank Fox",
6
6
  "bin": {
7
7
  "cpilot": "bin/run.js"
@@ -70,7 +70,8 @@
70
70
  "projects",
71
71
  "scripts",
72
72
  "README.md",
73
- "LICENSE"
73
+ "LICENSE",
74
+ "!**/source-nodes.env"
74
75
  ],
75
76
  "homepage": "https://github.com/Constellation-Labs/node-pilot",
76
77
  "keywords": [
@@ -3,10 +3,10 @@ networks:
3
3
  driver: bridge
4
4
 
5
5
  services:
6
- autoheal:
7
- image: willfarrell/autoheal
8
- volumes:
9
- - /var/run/docker.sock:/var/run/docker.sock
6
+ # autoheal:
7
+ # image: willfarrell/autoheal
8
+ # volumes:
9
+ # - /var/run/docker.sock:/var/run/docker.sock
10
10
  gl0:
11
11
  image: constellationnetwork/tessellation${CL_DOCKER_CORE_REPO:-}:${DOCKER_IMAGE_VERSION:-latest}
12
12
  networks:
@@ -30,8 +30,8 @@ services:
30
30
  timeout: 15s
31
31
  start_period: 60s
32
32
  profiles: ["gl0"]
33
- labels:
34
- autoheal: "true"
33
+ # labels:
34
+ # autoheal: "true"
35
35
  gl1:
36
36
  image: constellationnetwork/tessellation${CL_DOCKER_CORE_REPO:-}:${DOCKER_IMAGE_VERSION:-latest}
37
37
  restart: unless-stopped
@@ -46,10 +46,11 @@ services:
46
46
  - ./gl1/data:/app/data
47
47
  - ./gl1/logs:/app/logs
48
48
  - ./key.p12:/app/key.p12:ro
49
- # healthcheck:
50
- # test: ["CMD", "/app/internal-health-check.sh"]
51
- # interval: 3s
52
- # timeout: 5s
53
- # retries: 5
54
- # start_period: 10s
49
+ healthcheck:
50
+ test: ["CMD", "cpilotHC"]
51
+ # test: ["CMD", "/health-check/bin/run.sh"]
52
+ interval: 15s
53
+ retries: 1
54
+ timeout: 15s
55
+ start_period: 60s
55
56
  profiles: ["gl1"]
@@ -11,7 +11,7 @@ check_java_home() {
11
11
  JAVA_HOME_LINE='export JAVA_HOME="$(dirname "$(dirname "$(readlink -f "$(which java)")")")"'
12
12
  echo "JAVA_HOME is not set. Attempting to set automatically"
13
13
  echo "Please ensure the following line is in your ~/.bashrc or ~/.zshrc file:"
14
- echo "Script will attempt to now add it for you and run it, but this only adds to .bashrc"
14
+ echo "Script will attempt to now add it for you and run it, but this only adds to .bashrc"
15
15
  echo $JAVA_HOME_LINE
16
16
  echo "$JAVA_HOME_LINE" >> $HOME/.bashrc
17
17
  echo "Adding JAVA_HOME to current environment"
@@ -215,94 +215,6 @@ check_curl() {
215
215
  return 0
216
216
  }
217
217
 
218
- # Check and install Docker Engine
219
- check_docker() {
220
- # echo "Checking for Docker..."
221
- if command -v docker >/dev/null 2>&1; then
222
- echo "✅ Docker is already installed."
223
- return 0
224
- fi
225
-
226
- echo "⚠️ Docker not found. Will attempt to install Docker."
227
-
228
- case "$(uname)" in
229
- Linux)
230
- if command -v apt >/dev/null 2>&1; then
231
- echo "Installing Docker using script..."
232
- curl -fsSL https://get.docker.com -o get-docker.sh
233
- sudo sh ./get-docker.sh
234
- sudo usermod -aG docker $USER
235
- echo "Docker installed. You may need to log out and back in for group changes to take effect."
236
- else
237
- echo "⚠️ Unsupported Linux distribution. Please install Docker manually."
238
- return 1
239
- fi
240
- ;;
241
- Darwin)
242
- echo "Please install Docker Desktop manually from https://www.docker.com/products/docker-desktop"
243
- return 1
244
- ;;
245
- MINGW*|MSYS*|CYGWIN*)
246
- echo "On Windows, please install Docker Desktop manually from https://www.docker.com/products/docker-desktop"
247
- return 1
248
- ;;
249
- *)
250
- echo "⚠️ Unsupported OS: $(uname). Please install Docker manually."
251
- return 1
252
- ;;
253
- esac
254
-
255
- echo "✅ Docker installation complete."
256
- return 0
257
- }
258
-
259
- # Check and install Node.js
260
- check_node() {
261
- # echo "Checking for Node.js..."
262
- if [ -d "$HOME/.nvm" ]; then
263
- echo "✅ Node.js is already installed."
264
- return 0
265
- fi
266
-
267
- echo "⚠️ Node.js not found. Will attempt to install Node.js."
268
-
269
- case "$(uname)" in
270
- Linux)
271
- echo "Installing Node.js using nvm..."
272
- # NPM
273
- curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.3/install.sh | bash
274
-
275
- # Load nvm without needing to open a new terminal
276
- export NVM_DIR="$HOME/.nvm"
277
- [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm
278
- [ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion" # This loads nvm bash_completion
279
-
280
- # Install node
281
- nvm install 22
282
-
283
- # Verify installation
284
- echo "Node version: $(node -v)"
285
- echo "NPM version: $(npm -v)"
286
- echo "NPX version: $(npx -v)"
287
- ;;
288
- Darwin)
289
- if command -v brew >/dev/null 2>&1; then
290
- echo "Installing Node.js using Homebrew..."
291
- brew install node
292
- else
293
- echo "⚠️ Homebrew not found. Please install Node.js manually."
294
- return 1
295
- fi
296
- ;;
297
- *)
298
- echo "⚠️ No Node.js installation needed for this OS: $(uname)"
299
- return 0
300
- ;;
301
- esac
302
-
303
- echo "✅ Node.js installation complete."
304
- return 0
305
- }
306
218
 
307
219
  # Run all checks
308
220
  echo "🔍 Checking and installing required dependencies..."
@@ -311,7 +223,5 @@ check_java_home
311
223
  check_jq
312
224
  check_wget
313
225
  check_curl
314
- check_docker
315
- check_node
316
226
 
317
227
  echo "All dependency checks completed."
@@ -103,6 +103,11 @@ download_asset() {
103
103
 
104
104
  # Download the asset
105
105
  echo "$ASSET_NAME -> $OUTPUT_NAME"
106
+ # DEBUG=${DEBUG:-false}
107
+ # SILENT="-s"
108
+ # if [ "$DEBUG" = "true" ]; then
109
+ # SILENT="-#"
110
+ # fi
106
111
  curl -# -L -H "Accept: application/octet-stream" -o "$OUTPUT_DIR/$OUTPUT_NAME" "$DOWNLOAD_URL"
107
112
 
108
113
  if [ $? -ne 0 ]; then
@@ -1,70 +1,119 @@
1
1
  #!/usr/bin/env bash
2
2
 
3
+ # Find cpilot in PATH
3
4
  CPILOT=$(command -v cpilot)
4
5
  if [ -z "$CPILOT" ]; then
5
6
  echo "cpilot command not found. Please ensure Node Pilot is installed and accessible in your PATH."
6
7
  exit 1
7
8
  fi
8
9
 
9
- WORKING_DIR=/home/$USER/.config/systemd/user
10
+ # Get current user and home directory reliably
11
+ CURRENT_USER=$(id -un)
12
+ if [ "$CURRENT_USER" = "root" ]; then
13
+ IS_ROOT=true
14
+ WORKING_DIR="/etc/systemd/system"
15
+ HOME_DIR="/root"
16
+ UNIT_TARGET="multi-user.target"
17
+ else
18
+ IS_ROOT=false
19
+ HOME_DIR=$(eval echo ~$CURRENT_USER)
20
+ WORKING_DIR="$HOME_DIR/.config/systemd/user"
21
+ UNIT_TARGET="default.target"
22
+ fi
23
+
24
+ BIN_DIRECTORY="$HOME_DIR/.node-pilot/scripts"
10
25
 
11
- mkdir -p $WORKING_DIR
26
+ mkdir -p "$BIN_DIRECTORY"
12
27
 
13
- cat << EOF > $WORKING_DIR/restart-unhealthy.service
28
+ # Create working directory if it doesn't exist
29
+ mkdir -p "$WORKING_DIR"
30
+
31
+ # Create restart-unhealthy.service
32
+ cat << EOF > "$WORKING_DIR/restart-unhealthy.service"
14
33
  [Unit]
15
34
  Description=Restart unhealthy docker containers
16
35
  After=docker.service
17
36
  Wants=docker.service
18
37
 
19
38
  [Service]
20
- ExecStartPre=/bin/sleep 300
21
- ExecStart=$WORKING_DIR/restart-unhealthy.sh
22
- Restart=always
23
- RestartSec=60s
39
+ Type=oneshot
40
+ ExecStart=$BIN_DIRECTORY/restart-unhealthy.sh
41
+ EOF
42
+
43
+ # Create restart-unhealthy.timer
44
+ cat <<EOF > "$WORKING_DIR/restart-unhealthy.timer"
45
+ [Unit]
46
+ Description=Run docker unhealthy restart every 5 minutes
47
+ Requires=restart-unhealthy.service
48
+ After=docker.service
49
+ Wants=docker.service
50
+
51
+ [Timer]
52
+ OnCalendar=*:0/1
53
+ Persistent=true
54
+
55
+ [Install]
56
+ WantedBy=timers.target
24
57
  EOF
25
58
 
26
- cat << EOF > $WORKING_DIR/restart-unhealthy.sh
59
+ # Create restart-unhealthy.sh
60
+ cat << EOF > "$BIN_DIRECTORY/restart-unhealthy.sh"
27
61
  #!/usr/bin/env bash
28
62
  docker ps -q -f health=unhealthy | xargs --no-run-if-empty docker restart
29
63
  EOF
30
64
 
31
- chmod +x $WORKING_DIR/restart-unhealthy.sh
65
+ chmod +x "$BIN_DIRECTORY/restart-unhealthy.sh"
32
66
 
33
- cat << EOF > $WORKING_DIR/node-pilot-autostart.service
67
+ # Create node-pilot-autostart.service
68
+ cat << EOF > "$WORKING_DIR/node-pilot-autostart.service"
34
69
  [Unit]
35
70
  Description=Constellation Network Node Auto Restart Service
36
- After=default.target
71
+ After=$UNIT_TARGET
37
72
 
38
73
  [Service]
39
- ExecStart=$CPILOT restart --autostart
74
+ ExecStart=cpilot restart --autostart
40
75
 
41
76
  [Install]
42
- WantedBy=default.target
77
+ WantedBy=$UNIT_TARGET
43
78
  EOF
44
79
 
45
- cat << EOF > $WORKING_DIR/node-pilot-update.service
80
+ # Create node-pilot-update.service
81
+ cat << EOF > "$WORKING_DIR/node-pilot-update.service"
46
82
  [Unit]
47
83
  Description=Constellation Node Pilot Updater Service
48
- After=default.target
84
+ After=$UNIT_TARGET
49
85
 
50
86
  [Service]
51
- ExecStartPre=/bin/sleep 300
52
- ExecStart=$CPILOT restart --update
87
+ ExecStart=cpilot restart --update
53
88
  Restart=always
54
89
  RestartSec=5m
55
90
 
56
91
  [Install]
57
- WantedBy=default.target
92
+ WantedBy=$UNIT_TARGET
58
93
  EOF
59
94
 
60
- systemctl --user daemon-reload
61
-
62
- systemctl --user enable restart-unhealthy.service
63
- systemctl --user enable node-pilot-autostart.service
64
- systemctl --user enable node-pilot-update.service
65
-
66
- systemctl --user start restart-unhealthy.service
67
- systemctl --user start node-pilot-autostart.service
68
- systemctl --user start node-pilot-update.service
69
-
70
- loginctl enable-linger $USER
95
+ # Enable and start services/timers
96
+ if [ "$IS_ROOT" = true ]; then
97
+ systemctl daemon-reload
98
+ systemctl enable restart-unhealthy.timer
99
+ systemctl enable node-pilot-autostart.service
100
+ systemctl enable node-pilot-update.service
101
+
102
+ systemctl start restart-unhealthy.timer
103
+ systemctl start node-pilot-autostart.service
104
+ systemctl start node-pilot-update.service
105
+ else
106
+ systemctl --user daemon-reload
107
+ systemctl --user enable restart-unhealthy.timer
108
+ systemctl --user enable node-pilot-autostart.service
109
+ systemctl --user enable node-pilot-update.service
110
+
111
+ systemctl --user start restart-unhealthy.timer
112
+ systemctl --user start node-pilot-autostart.service
113
+ systemctl --user start node-pilot-update.service
114
+
115
+ # Enable linger for the user to allow user services to run without login
116
+ loginctl enable-linger "$CURRENT_USER"
117
+ fi
118
+ #systemctl --user status node-pilot-update.service
119
+ # systemctl --user status restart-unhealthy
@@ -0,0 +1,11 @@
1
+ [Unit]
2
+ Description=Constellation Node Pilot Updater Service
3
+ After=default.target
4
+
5
+ [Service]
6
+ ExecStart=/bin/bash -lc "echo $HOME : $(which node) >> /home/ubuntu/service.log"
7
+ Restart=always
8
+ RestartSec=5m
9
+
10
+ [Install]
11
+ WantedBy=default.target
@@ -1,9 +0,0 @@
1
- SOURCE_NODE_1_PORT=9000
2
- SOURCE_NODE_1_HOST=13.52.205.240
3
- SOURCE_NODE_1_ID=e2f4496e5872682d7a55aa06e507a58e96b5d48a5286bfdff7ed780fa464d9e789b2760ecd840f4cb3ee6e1c1d81b2ee844c88dbebf149b1084b7313eb680714
4
- SOURCE_NODE_2_PORT=9000
5
- SOURCE_NODE_2_HOST=50.18.155.22
6
- SOURCE_NODE_2_ID=3458a688925a4bd89f2ac2c695362e44d2e0c2903bdbb41b341a4d39283b22d8c85b487bd33cc5d36dbe5e31b5b00a10a6eab802718ead4ed7192ade5a5d1941
7
- SOURCE_NODE_3_PORT=9000
8
- SOURCE_NODE_3_HOST=52.9.216.57
9
- SOURCE_NODE_3_ID=46daea11ca239cb8c0c8cdeb27db9dbe9c03744908a8a389a60d14df2ddde409260a93334d74957331eec1af323f458b12b3a6c3b8e05885608aae7e3a77eac7
@@ -1,9 +0,0 @@
1
- SOURCE_NODE_1_PORT=9000
2
- SOURCE_NODE_1_HOST=52.53.46.33
3
- SOURCE_NODE_1_ID=e0c1ee6ec43510f0e16d2969a7a7c074a5c8cdb477c074fe9c32a9aad8cbc8ff1dff60bb81923e0db437d2686a9b65b86c403e6a21fa32b6acc4e61be4d70925
4
- SOURCE_NODE_2_PORT=9000
5
- SOURCE_NODE_2_HOST=54.215.18.98
6
- SOURCE_NODE_2_ID=629880a5b8d4cc6d12aec26f24230a463825c429723153aeaff29475b29e39d2406af0f8b034ba7798ae598dbd5f513d642bcbbeef088290abeadac61a0445d6
7
- SOURCE_NODE_3_PORT=9000
8
- SOURCE_NODE_3_HOST=54.151.19.111
9
- SOURCE_NODE_3_ID=710b3dc521b805aea7a798d61f5d4dae39601124f1f34fac9738a78047adeff60931ba522250226b87a2194d3b7d39da8d2cbffa35d6502c70f1a7e97132a4b0
@@ -1,9 +0,0 @@
1
- SOURCE_NODE_1_PORT=9000
2
- SOURCE_NODE_1_HOST=52.8.132.193
3
- SOURCE_NODE_1_ID=e2f4496e5872682d7a55aa06e507a58e96b5d48a5286bfdff7ed780fa464d9e789b2760ecd840f4cb3ee6e1c1d81b2ee844c88dbebf149b1084b7313eb680714
4
- SOURCE_NODE_2_PORT=9000
5
- SOURCE_NODE_2_HOST=13.57.110.45
6
- SOURCE_NODE_2_ID=3458a688925a4bd89f2ac2c695362e44d2e0c2903bdbb41b341a4d39283b22d8c85b487bd33cc5d36dbe5e31b5b00a10a6eab802718ead4ed7192ade5a5d1941
7
- SOURCE_NODE_3_PORT=9000
8
- SOURCE_NODE_3_HOST=54.153.23.79
9
- SOURCE_NODE_3_ID=46daea11ca239cb8c0c8cdeb27db9dbe9c03744908a8a389a60d14df2ddde409260a93334d74957331eec1af323f458b12b3a6c3b8e05885608aae7e3a77eac7