Skip to content

Commit

Permalink
Merge pull request #91 from stake-house/feature/config-page
Browse files Browse the repository at this point in the history
Add Configuration and Install pages
  • Loading branch information
remyroy authored Jun 10, 2022
2 parents fc0458a + e38a7ec commit 359f79b
Show file tree
Hide file tree
Showing 18 changed files with 1,620 additions and 681 deletions.
42 changes: 42 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# credit https://trigodev.com/blog/develop-electron-in-docker
FROM node:16.15.0


RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install \
build-essential clang policykit-1 \
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 \
xorg openbox libatk-adaptor \
gperf bison python3-dbusmock \
libnss3-dev gcc-multilib g++-multilib curl sudo \
-yq --no-install-suggests --no-install-recommends \
&& apt-get clean && rm -rf /var/lib/apt/lists/* \
ca-certificates \
curl \
gnupg \
lsb-release

RUN curl -fsSL https://get.docker.com | sh
# RUN echo 'alias docker-compose="docker compose"' >> /home/node/.bashrc
RUN apt-get install docker-compose


WORKDIR /app
COPY . .
RUN chown -R node /app

# install node modules and perform an electron rebuild
USER node
RUN npm install
RUN npx electron-rebuild

USER root
RUN chown root /app/node_modules/electron/dist/chrome-sandbox
RUN chmod 4755 /app/node_modules/electron/dist/chrome-sandbox
RUN echo '%sudo ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers
RUN usermod -aG sudo node
RUN usermod -aG docker node


USER node
ENTRYPOINT [ "bash" ]
# 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
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "wagyuinstaller",
"productName": "Wagyu Installer",
"version": "0.5.0",
"version": "0.5.3",
"description": "Application aimed at lowering the technical bar to staking on Ethereum",
"main": "./build/electron/index.js",
"author": "Colfax Selby <[email protected]>",
Expand Down
88 changes: 70 additions & 18 deletions src/electron/EthDockerInstaller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { generate as generate_password } from "generate-password";

import { exec, execFile } from 'child_process';
import { promisify } from 'util';
import { withDir } from 'tmp-promise'
import { withDir } from 'tmp-promise';

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

Expand All @@ -18,7 +18,8 @@ import {
IMultiClientInstaller,
NodeStatus,
ValidatorStatus,
InstallDetails
InstallDetails,
OutputLogs
} from "./IMultiClientInstaller";
import { Network, networkToExecution } from '../react/types';
import { doesFileExist, doesDirectoryExist } from './BashUtils';
Expand All @@ -40,47 +41,74 @@ type SystemdServiceDetails = {
unitFileState: string | undefined;
}

const writeOutput = (message: string, outputLogs?: OutputLogs): void => {
if (outputLogs) {
outputLogs(message);
}
};

export class EthDockerInstaller implements IMultiClientInstaller {

title = 'Electron';

async preInstall(): Promise<boolean> {
async preInstall(outputLogs?: OutputLogs): Promise<boolean> {

let packagesToInstall = new Array<string>();

// We need git installed
const gitPackageName = 'git';

writeOutput(`Checking if ${gitPackageName} is installed.`, outputLogs);
const gitInstalled = await this.checkForInstalledUbuntuPackage(gitPackageName);
if (!gitInstalled) {
writeOutput(`${gitPackageName} is not installed. We will install it.`, outputLogs);
packagesToInstall.push(gitPackageName);
} else {
writeOutput(`${gitPackageName} is already installed. We will not install it.`, outputLogs);
}

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

writeOutput(`Checking if ${dockerPackageName} is installed.`, outputLogs);
const dockerInstalled = await this.checkForInstalledUbuntuPackage(dockerPackageName);
if (!dockerInstalled) {
writeOutput(`${dockerPackageName} is not installed. We will install it.`, outputLogs);
packagesToInstall.push(dockerPackageName);
} else {
writeOutput(`${dockerPackageName} is already installed. We will not install it.`, outputLogs);

writeOutput(`Checking if we need to enable or start the ${dockerServiceName} service.`, outputLogs);
const dockerServiceDetails = await this.getSystemdServiceDetails(dockerServiceName);
needToEnableDockerService = dockerServiceDetails.unitFileState != 'enabled';
if (needToEnableDockerService) {
writeOutput(`The ${dockerServiceName} service is not enabled. We will enable it.`, outputLogs);
}
needToStartDockerService = dockerServiceDetails.subState != 'running';
if (needToStartDockerService) {
writeOutput(`The ${dockerServiceName} service is not started. We will start it.`, outputLogs);
}
}

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

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

return await this.preInstallAdminScript(
packagesToInstall,
needUserInDockerGroup,
needToEnableDockerService,
needToStartDockerService);
needToStartDockerService,
outputLogs);
}

async getSystemdServiceDetails(serviceName: string): Promise<SystemdServiceDetails> {
Expand Down Expand Up @@ -132,7 +160,8 @@ export class EthDockerInstaller implements IMultiClientInstaller {
packagesToInstall: Array<string>,
needUserInDockerGroup: boolean,
needToEnableDockerService: boolean,
needToStartDockerService: boolean): Promise<boolean> {
needToStartDockerService: boolean,
outputLogs?: OutputLogs): Promise<boolean> {

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

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

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

// Install APT packages
if (packagesToInstall.length > 0) {
await scriptFile.write('apt -y update\n');
await scriptFile.write('apt -y install ' + commandJoin(packagesToInstall) + '\n');
const aptUpdate = 'apt -y update\n';
const aptInstall = 'apt -y install ' + commandJoin(packagesToInstall) + '\n';

scriptContent += aptUpdate + aptInstall;

await scriptFile.write(aptUpdate);
await scriptFile.write(aptInstall);
}

// Enable docker service
if (needToEnableDockerService) {
await scriptFile.write('systemctl enable --now ' + commandJoin([dockerServiceName]) + '\n');
const systemctlEnable = 'systemctl enable --now ' + commandJoin([dockerServiceName]) + '\n';

scriptContent += systemctlEnable;

await scriptFile.write(systemctlEnable);
}

// Start docker service
if (needToStartDockerService) {
await scriptFile.write('systemctl start ' + commandJoin([dockerServiceName]) + '\n');
const systemctlStart = 'systemctl start ' + commandJoin([dockerServiceName]) + '\n';

scriptContent += systemctlStart;

await scriptFile.write(systemctlStart);
}

// Add user in docker group
if (needUserInDockerGroup) {
const userName = await this.getUsername();
const usermodUser = `usermod -aG ${dockerGroupName} ${userName}\n`;

scriptContent += usermodUser;

await scriptFile.write(`usermod -aG ${dockerGroupName} ${userName}\n`);
await scriptFile.write(usermodUser);
}

await scriptFile.chmod(0o500);
await scriptFile.close();

writeOutput(`Running script ${scriptPath} with the following content as root:\n${scriptContent}`, outputLogs);

const promise = new Promise<boolean>(async (resolve, reject) => {
const options = {
name: this.title
Expand All @@ -186,6 +234,9 @@ export class EthDockerInstaller implements IMultiClientInstaller {
sudo.exec(scriptPath, options,
function (error, stdout, stderr) {
if (error) reject(error);
if (stdout) {
writeOutput(stdout.toString(), outputLogs);
}
resolve(true);
}
);
Expand Down Expand Up @@ -230,7 +281,7 @@ export class EthDockerInstaller implements IMultiClientInstaller {
return stdout.indexOf('[installed]') > 0
}

async install(details: InstallDetails): Promise<boolean> {
async install(details: InstallDetails, outputLogs?: OutputLogs): Promise<boolean> {
// Install and update eth-docker
if (!await this.installUpdateEthDockerCode(details.network)) {
return false;
Expand Down Expand Up @@ -422,11 +473,11 @@ EONG
return true;
}

async postInstall(network: Network): Promise<boolean> {
return this.startNodes(network);
async postInstall(network: Network, outputLogs?: OutputLogs): Promise<boolean> {
return this.startNodes(network, outputLogs);
}

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

Expand All @@ -448,7 +499,7 @@ EONG
return true;
}

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

Expand All @@ -470,13 +521,13 @@ EONG
return true;
}

async updateExecutionClient(): Promise<void> {
async updateExecutionClient(outputLogs?: OutputLogs): Promise<void> {
// TODO: implement
console.log("Executing updateExecutionClient");
return;
}

async updateConsensusClient(): Promise<void> {
async updateConsensusClient(outputLogs?: OutputLogs): Promise<void> {
// TODO: implement
console.log("Executing updateConsensusClient");
return;
Expand All @@ -485,7 +536,8 @@ EONG
async importKeys(
network: Network,
keyStoreDirectoryPath: string,
keyStorePassword: string): Promise<boolean> {
keyStorePassword: string,
outputLogs?: OutputLogs): Promise<boolean> {

const networkPath = path.join(installPath, network.toLowerCase());
const ethDockerPath = path.join(networkPath, 'eth-docker');
Expand Down
29 changes: 17 additions & 12 deletions src/electron/IMultiClientInstaller.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,29 @@
import { Network } from "../react/types"
import { Network } from "../react/types";

export interface IMultiClientInstaller {

// Functionality
preInstall: () => Promise<boolean>,
install: (details: InstallDetails) => Promise<boolean>,
postInstall: (network: Network) => Promise<boolean>,
preInstall: (outputLogs?: OutputLogs) => Promise<boolean>,
install: (details: InstallDetails, outputLogs?: OutputLogs) => Promise<boolean>,
postInstall: (network: Network, outputLogs?: OutputLogs) => Promise<boolean>,

stopNodes: (network: Network) => Promise<boolean>,
startNodes: (network: Network) => Promise<boolean>,
stopNodes: (network: Network, outputLogs?: OutputLogs) => Promise<boolean>,
startNodes: (network: Network, outputLogs?: OutputLogs) => Promise<boolean>,

updateExecutionClient: () => Promise<void>,
updateConsensusClient: () => Promise<void>,
updateExecutionClient: (outputLogs?: OutputLogs) => Promise<void>,
updateConsensusClient: (outputLogs?: OutputLogs) => Promise<void>,

importKeys: (
network: Network,
keyStoreDirectoryPath: string,
keyStorePassword: string) => Promise<boolean>,
keyStorePassword: string,
outputLogs?: OutputLogs) => Promise<boolean>,
exportKeys: () => Promise<void>,

switchExecutionClient: (targetClient: ExecutionClient) => Promise<boolean>,
switchConsensusClient: (targetClient: ConsensusClient) => Promise<boolean>,
switchExecutionClient: (targetClient: ExecutionClient, outputLogs?: OutputLogs) => Promise<boolean>,
switchConsensusClient: (targetClient: ConsensusClient, outputLogs?: OutputLogs) => Promise<boolean>,

uninstall: () => Promise<boolean>,
uninstall: (outputLogs?: OutputLogs) => Promise<boolean>,


// Data
Expand Down Expand Up @@ -50,6 +51,10 @@ export interface IMultiClientInstaller {
// TODO: logs stream
}

export interface OutputLogs {
(message: string): void;
}

export type InstallDetails = {
network: Network,
executionClient: ExecutionClient,
Expand Down
8 changes: 5 additions & 3 deletions src/electron/preload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,13 @@ import { Network } from "../react/types";
import { doesDirectoryExist, findFirstFile } from './BashUtils';

import { EthDockerInstaller } from './EthDockerInstaller';
import { InstallDetails } from "./IMultiClientInstaller";
import { InstallDetails, OutputLogs } from "./IMultiClientInstaller";

import { Writable } from 'stream';

const ethDockerInstaller = new EthDockerInstaller();
const ethDockerPreInstall = async (): Promise<boolean> => {
return ethDockerInstaller.preInstall();
const ethDockerPreInstall = async (outputLogs?: OutputLogs): Promise<boolean> => {
return ethDockerInstaller.preInstall(outputLogs);
};
const ethDockerInstall = async (details: InstallDetails): Promise<boolean> => {
return ethDockerInstaller.install(details);
Expand Down
4 changes: 2 additions & 2 deletions src/electron/renderer.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import {
import {
EthDockerInstaller
} from './EthDockerInstaller'
import { InstallDetails } from "./IMultiClientInstaller";
import { InstallDetails, OutputLogs } from "./IMultiClientInstaller";

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


export interface IEthDockerAPI {
preInstall: () => Promise<boolean>,
preInstall: (outputLogs?: OutputLogs) => Promise<boolean>,
install: (details: InstallDetails) => Promise<boolean>,
importKeys: (
network: Network,
Expand Down
Loading

0 comments on commit 359f79b

Please sign in to comment.