Skip to content

Commit 359f79b

Browse files
authored
Merge pull request #91 from stake-house/feature/config-page
Add Configuration and Install pages
2 parents fc0458a + e38a7ec commit 359f79b

18 files changed

+1620
-681
lines changed

Dockerfile

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# credit https://trigodev.com/blog/develop-electron-in-docker
2+
FROM node:16.15.0
3+
4+
5+
RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install \
6+
build-essential clang policykit-1 \
7+
git libx11-xcb1 libxcb-dri3-0 libcups2-dev libxtst-dev libatk-bridge2.0-0 libdbus-1-dev libgtk-3-dev libxss1 libnotify-dev libasound2-dev libcap-dev libdrm2 libice6 libsm6 \
8+
xorg openbox libatk-adaptor \
9+
gperf bison python3-dbusmock \
10+
libnss3-dev gcc-multilib g++-multilib curl sudo \
11+
-yq --no-install-suggests --no-install-recommends \
12+
&& apt-get clean && rm -rf /var/lib/apt/lists/* \
13+
ca-certificates \
14+
curl \
15+
gnupg \
16+
lsb-release
17+
18+
RUN curl -fsSL https://get.docker.com | sh
19+
# RUN echo 'alias docker-compose="docker compose"' >> /home/node/.bashrc
20+
RUN apt-get install docker-compose
21+
22+
23+
WORKDIR /app
24+
COPY . .
25+
RUN chown -R node /app
26+
27+
# install node modules and perform an electron rebuild
28+
USER node
29+
RUN npm install
30+
RUN npx electron-rebuild
31+
32+
USER root
33+
RUN chown root /app/node_modules/electron/dist/chrome-sandbox
34+
RUN chmod 4755 /app/node_modules/electron/dist/chrome-sandbox
35+
RUN echo '%sudo ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers
36+
RUN usermod -aG sudo node
37+
RUN usermod -aG docker node
38+
39+
40+
USER node
41+
ENTRYPOINT [ "bash" ]
42+
# docker run --privileged -v /var/run/docker.sock:/var/run/docker.sock -v /tmp/.X11-unix:/tmp/.X11-unix -e DISPLAY=unix$DISPLAY -v `pwd`/src:/app/src --rm -it electron-wrapper bash

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "wagyuinstaller",
33
"productName": "Wagyu Installer",
4-
"version": "0.5.0",
4+
"version": "0.5.3",
55
"description": "Application aimed at lowering the technical bar to staking on Ethereum",
66
"main": "./build/electron/index.js",
77
"author": "Colfax Selby <[email protected]>",

src/electron/EthDockerInstaller.ts

Lines changed: 70 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { generate as generate_password } from "generate-password";
55

66
import { exec, execFile } from 'child_process';
77
import { promisify } from 'util';
8-
import { withDir } from 'tmp-promise'
8+
import { withDir } from 'tmp-promise';
99

1010
import { open, rm, mkdir } from 'fs/promises';
1111

@@ -18,7 +18,8 @@ import {
1818
IMultiClientInstaller,
1919
NodeStatus,
2020
ValidatorStatus,
21-
InstallDetails
21+
InstallDetails,
22+
OutputLogs
2223
} from "./IMultiClientInstaller";
2324
import { Network, networkToExecution } from '../react/types';
2425
import { doesFileExist, doesDirectoryExist } from './BashUtils';
@@ -40,47 +41,74 @@ type SystemdServiceDetails = {
4041
unitFileState: string | undefined;
4142
}
4243

44+
const writeOutput = (message: string, outputLogs?: OutputLogs): void => {
45+
if (outputLogs) {
46+
outputLogs(message);
47+
}
48+
};
49+
4350
export class EthDockerInstaller implements IMultiClientInstaller {
4451

4552
title = 'Electron';
4653

47-
async preInstall(): Promise<boolean> {
54+
async preInstall(outputLogs?: OutputLogs): Promise<boolean> {
4855

4956
let packagesToInstall = new Array<string>();
5057

5158
// We need git installed
5259
const gitPackageName = 'git';
5360

61+
writeOutput(`Checking if ${gitPackageName} is installed.`, outputLogs);
5462
const gitInstalled = await this.checkForInstalledUbuntuPackage(gitPackageName);
5563
if (!gitInstalled) {
64+
writeOutput(`${gitPackageName} is not installed. We will install it.`, outputLogs);
5665
packagesToInstall.push(gitPackageName);
66+
} else {
67+
writeOutput(`${gitPackageName} is already installed. We will not install it.`, outputLogs);
5768
}
5869

5970
// We need docker installed, enabled and running
6071
const dockerPackageName = 'docker-compose';
6172
let needToEnableDockerService = true;
6273
let needToStartDockerService = false;
6374

75+
writeOutput(`Checking if ${dockerPackageName} is installed.`, outputLogs);
6476
const dockerInstalled = await this.checkForInstalledUbuntuPackage(dockerPackageName);
6577
if (!dockerInstalled) {
78+
writeOutput(`${dockerPackageName} is not installed. We will install it.`, outputLogs);
6679
packagesToInstall.push(dockerPackageName);
6780
} else {
81+
writeOutput(`${dockerPackageName} is already installed. We will not install it.`, outputLogs);
82+
83+
writeOutput(`Checking if we need to enable or start the ${dockerServiceName} service.`, outputLogs);
6884
const dockerServiceDetails = await this.getSystemdServiceDetails(dockerServiceName);
6985
needToEnableDockerService = dockerServiceDetails.unitFileState != 'enabled';
86+
if (needToEnableDockerService) {
87+
writeOutput(`The ${dockerServiceName} service is not enabled. We will enable it.`, outputLogs);
88+
}
7089
needToStartDockerService = dockerServiceDetails.subState != 'running';
90+
if (needToStartDockerService) {
91+
writeOutput(`The ${dockerServiceName} service is not started. We will start it.`, outputLogs);
92+
}
7193
}
7294

7395
// We need our user to be in docker group
96+
writeOutput(`Checking if current user is in ${dockerGroupName} group.`, outputLogs);
7497
const needUserInDockerGroup = !await this.isUserInGroup(dockerGroupName);
98+
if (needUserInDockerGroup) {
99+
writeOutput(`Current user is not in ${dockerGroupName} group. We will add it.`, outputLogs);
100+
}
75101

76102
// We need our installPath directory
103+
writeOutput(`Creating install directory in ${installPath}.`, outputLogs);
77104
await mkdir(installPath, { recursive: true });
78105

79106
return await this.preInstallAdminScript(
80107
packagesToInstall,
81108
needUserInDockerGroup,
82109
needToEnableDockerService,
83-
needToStartDockerService);
110+
needToStartDockerService,
111+
outputLogs);
84112
}
85113

86114
async getSystemdServiceDetails(serviceName: string): Promise<SystemdServiceDetails> {
@@ -132,7 +160,8 @@ export class EthDockerInstaller implements IMultiClientInstaller {
132160
packagesToInstall: Array<string>,
133161
needUserInDockerGroup: boolean,
134162
needToEnableDockerService: boolean,
135-
needToStartDockerService: boolean): Promise<boolean> {
163+
needToStartDockerService: boolean,
164+
outputLogs?: OutputLogs): Promise<boolean> {
136165

137166
if (
138167
packagesToInstall.length > 0 ||
@@ -148,36 +177,55 @@ export class EthDockerInstaller implements IMultiClientInstaller {
148177
await withDir(async dirResult => {
149178

150179
const scriptPath = path.join(dirResult.path, 'commands.sh');
180+
let scriptContent = '';
151181

152182
const scriptFile = await open(scriptPath, 'w');
153183
await scriptFile.write('#!/bin/bash\n');
154184

155185
// Install APT packages
156186
if (packagesToInstall.length > 0) {
157-
await scriptFile.write('apt -y update\n');
158-
await scriptFile.write('apt -y install ' + commandJoin(packagesToInstall) + '\n');
187+
const aptUpdate = 'apt -y update\n';
188+
const aptInstall = 'apt -y install ' + commandJoin(packagesToInstall) + '\n';
189+
190+
scriptContent += aptUpdate + aptInstall;
191+
192+
await scriptFile.write(aptUpdate);
193+
await scriptFile.write(aptInstall);
159194
}
160195

161196
// Enable docker service
162197
if (needToEnableDockerService) {
163-
await scriptFile.write('systemctl enable --now ' + commandJoin([dockerServiceName]) + '\n');
198+
const systemctlEnable = 'systemctl enable --now ' + commandJoin([dockerServiceName]) + '\n';
199+
200+
scriptContent += systemctlEnable;
201+
202+
await scriptFile.write(systemctlEnable);
164203
}
165204

166205
// Start docker service
167206
if (needToStartDockerService) {
168-
await scriptFile.write('systemctl start ' + commandJoin([dockerServiceName]) + '\n');
207+
const systemctlStart = 'systemctl start ' + commandJoin([dockerServiceName]) + '\n';
208+
209+
scriptContent += systemctlStart;
210+
211+
await scriptFile.write(systemctlStart);
169212
}
170213

171214
// Add user in docker group
172215
if (needUserInDockerGroup) {
173216
const userName = await this.getUsername();
217+
const usermodUser = `usermod -aG ${dockerGroupName} ${userName}\n`;
218+
219+
scriptContent += usermodUser;
174220

175-
await scriptFile.write(`usermod -aG ${dockerGroupName} ${userName}\n`);
221+
await scriptFile.write(usermodUser);
176222
}
177223

178224
await scriptFile.chmod(0o500);
179225
await scriptFile.close();
180226

227+
writeOutput(`Running script ${scriptPath} with the following content as root:\n${scriptContent}`, outputLogs);
228+
181229
const promise = new Promise<boolean>(async (resolve, reject) => {
182230
const options = {
183231
name: this.title
@@ -186,6 +234,9 @@ export class EthDockerInstaller implements IMultiClientInstaller {
186234
sudo.exec(scriptPath, options,
187235
function (error, stdout, stderr) {
188236
if (error) reject(error);
237+
if (stdout) {
238+
writeOutput(stdout.toString(), outputLogs);
239+
}
189240
resolve(true);
190241
}
191242
);
@@ -230,7 +281,7 @@ export class EthDockerInstaller implements IMultiClientInstaller {
230281
return stdout.indexOf('[installed]') > 0
231282
}
232283

233-
async install(details: InstallDetails): Promise<boolean> {
284+
async install(details: InstallDetails, outputLogs?: OutputLogs): Promise<boolean> {
234285
// Install and update eth-docker
235286
if (!await this.installUpdateEthDockerCode(details.network)) {
236287
return false;
@@ -422,11 +473,11 @@ EONG
422473
return true;
423474
}
424475

425-
async postInstall(network: Network): Promise<boolean> {
426-
return this.startNodes(network);
476+
async postInstall(network: Network, outputLogs?: OutputLogs): Promise<boolean> {
477+
return this.startNodes(network, outputLogs);
427478
}
428479

429-
async stopNodes(network: Network): Promise<boolean> {
480+
async stopNodes(network: Network, outputLogs?: OutputLogs): Promise<boolean> {
430481
const networkPath = path.join(installPath, network.toLowerCase());
431482
const ethDockerPath = path.join(networkPath, 'eth-docker');
432483

@@ -448,7 +499,7 @@ EONG
448499
return true;
449500
}
450501

451-
async startNodes(network: Network): Promise<boolean> {
502+
async startNodes(network: Network, outputLogs?: OutputLogs): Promise<boolean> {
452503
const networkPath = path.join(installPath, network.toLowerCase());
453504
const ethDockerPath = path.join(networkPath, 'eth-docker');
454505

@@ -470,13 +521,13 @@ EONG
470521
return true;
471522
}
472523

473-
async updateExecutionClient(): Promise<void> {
524+
async updateExecutionClient(outputLogs?: OutputLogs): Promise<void> {
474525
// TODO: implement
475526
console.log("Executing updateExecutionClient");
476527
return;
477528
}
478529

479-
async updateConsensusClient(): Promise<void> {
530+
async updateConsensusClient(outputLogs?: OutputLogs): Promise<void> {
480531
// TODO: implement
481532
console.log("Executing updateConsensusClient");
482533
return;
@@ -485,7 +536,8 @@ EONG
485536
async importKeys(
486537
network: Network,
487538
keyStoreDirectoryPath: string,
488-
keyStorePassword: string): Promise<boolean> {
539+
keyStorePassword: string,
540+
outputLogs?: OutputLogs): Promise<boolean> {
489541

490542
const networkPath = path.join(installPath, network.toLowerCase());
491543
const ethDockerPath = path.join(networkPath, 'eth-docker');

src/electron/IMultiClientInstaller.ts

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,29 @@
1-
import { Network } from "../react/types"
1+
import { Network } from "../react/types";
22

33
export interface IMultiClientInstaller {
44

55
// Functionality
6-
preInstall: () => Promise<boolean>,
7-
install: (details: InstallDetails) => Promise<boolean>,
8-
postInstall: (network: Network) => Promise<boolean>,
6+
preInstall: (outputLogs?: OutputLogs) => Promise<boolean>,
7+
install: (details: InstallDetails, outputLogs?: OutputLogs) => Promise<boolean>,
8+
postInstall: (network: Network, outputLogs?: OutputLogs) => Promise<boolean>,
99

10-
stopNodes: (network: Network) => Promise<boolean>,
11-
startNodes: (network: Network) => Promise<boolean>,
10+
stopNodes: (network: Network, outputLogs?: OutputLogs) => Promise<boolean>,
11+
startNodes: (network: Network, outputLogs?: OutputLogs) => Promise<boolean>,
1212

13-
updateExecutionClient: () => Promise<void>,
14-
updateConsensusClient: () => Promise<void>,
13+
updateExecutionClient: (outputLogs?: OutputLogs) => Promise<void>,
14+
updateConsensusClient: (outputLogs?: OutputLogs) => Promise<void>,
1515

1616
importKeys: (
1717
network: Network,
1818
keyStoreDirectoryPath: string,
19-
keyStorePassword: string) => Promise<boolean>,
19+
keyStorePassword: string,
20+
outputLogs?: OutputLogs) => Promise<boolean>,
2021
exportKeys: () => Promise<void>,
2122

22-
switchExecutionClient: (targetClient: ExecutionClient) => Promise<boolean>,
23-
switchConsensusClient: (targetClient: ConsensusClient) => Promise<boolean>,
23+
switchExecutionClient: (targetClient: ExecutionClient, outputLogs?: OutputLogs) => Promise<boolean>,
24+
switchConsensusClient: (targetClient: ConsensusClient, outputLogs?: OutputLogs) => Promise<boolean>,
2425

25-
uninstall: () => Promise<boolean>,
26+
uninstall: (outputLogs?: OutputLogs) => Promise<boolean>,
2627

2728

2829
// Data
@@ -50,6 +51,10 @@ export interface IMultiClientInstaller {
5051
// TODO: logs stream
5152
}
5253

54+
export interface OutputLogs {
55+
(message: string): void;
56+
}
57+
5358
export type InstallDetails = {
5459
network: Network,
5560
executionClient: ExecutionClient,

src/electron/preload.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,13 @@ import { Network } from "../react/types";
1616
import { doesDirectoryExist, findFirstFile } from './BashUtils';
1717

1818
import { EthDockerInstaller } from './EthDockerInstaller';
19-
import { InstallDetails } from "./IMultiClientInstaller";
19+
import { InstallDetails, OutputLogs } from "./IMultiClientInstaller";
20+
21+
import { Writable } from 'stream';
2022

2123
const ethDockerInstaller = new EthDockerInstaller();
22-
const ethDockerPreInstall = async (): Promise<boolean> => {
23-
return ethDockerInstaller.preInstall();
24+
const ethDockerPreInstall = async (outputLogs?: OutputLogs): Promise<boolean> => {
25+
return ethDockerInstaller.preInstall(outputLogs);
2426
};
2527
const ethDockerInstall = async (details: InstallDetails): Promise<boolean> => {
2628
return ethDockerInstaller.install(details);

src/electron/renderer.d.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ import {
2626
import {
2727
EthDockerInstaller
2828
} from './EthDockerInstaller'
29-
import { InstallDetails } from "./IMultiClientInstaller";
29+
import { InstallDetails, OutputLogs } from "./IMultiClientInstaller";
3030

3131
export interface IElectronAPI {
3232
shellOpenExternal: (url: string, options?: Electron.OpenExternalOptions | undefined) => Promise<void>,
@@ -44,7 +44,7 @@ export interface IBashUtilsAPI {
4444

4545

4646
export interface IEthDockerAPI {
47-
preInstall: () => Promise<boolean>,
47+
preInstall: (outputLogs?: OutputLogs) => Promise<boolean>,
4848
install: (details: InstallDetails) => Promise<boolean>,
4949
importKeys: (
5050
network: Network,

0 commit comments

Comments
 (0)