Skip to content

Commit

Permalink
Merge pull request #832 from dappnode/pablo/fullnode
Browse files Browse the repository at this point in the history
Add fullnode.dappnode domain to docker DNS
  • Loading branch information
dapplion authored Jul 30, 2021
2 parents 8316946 + 5a941f2 commit 0487a70
Show file tree
Hide file tree
Showing 14 changed files with 552 additions and 58 deletions.
1 change: 0 additions & 1 deletion packages/admin-ui/src/__mock-backend__/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,6 @@ export const otherCalls: Omit<Routes, keyof typeof namedSpacedCalls> = {
cleanDb: async () => {},
copyFileTo: async () => {},
diagnose: async () => [],
domainAliasSet: async () => {},
ethClientFallbackSet: async () => {},
ethClientTargetSet: async () => {},
ipfsTest: async () => {},
Expand Down
8 changes: 1 addition & 7 deletions packages/admin-ui/src/common/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -194,11 +194,6 @@ export interface Routes {
/** Returns docker engine update requirements */
dockerEngineUpdateCheck: () => Promise<DockerUpdateStatus>;

/**
* Set a domain alias to a DAppNode package by name
*/
domainAliasSet: (kwargs: { alias: string; dnpName: string }) => Promise<void>;

/**
* Sets if a fallback should be used
*/
Expand All @@ -211,7 +206,7 @@ export interface Routes {
*/
ethClientTargetSet: (kwargs: {
target: EthClientTarget;
deleteVolumes?: boolean;
deletePrevEthClient?: boolean;
}) => Promise<void>;

/**
Expand Down Expand Up @@ -621,7 +616,6 @@ export const routesData: { [P in keyof Routes]: RouteData } = {
dockerComposeUpdateCheck: {},
dockerEngineUpdate: { log: true },
dockerEngineUpdateCheck: {},
domainAliasSet: { log: true },
ethClientFallbackSet: { log: true },
ethClientTargetSet: { log: true },
fetchCoreUpdateData: {},
Expand Down
14 changes: 7 additions & 7 deletions packages/admin-ui/src/pages/system/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,21 +21,21 @@ export const changeEthClientTarget = (
if (nextTarget === prevTarget) return;

// If the previous target is package, ask the user if deleteVolumes
const deleteVolumes =
const deletePrevEthClient =
prevTarget && prevTarget !== "remote"
? await new Promise((resolve: (_deleteVolumes: boolean) => void) =>
? await new Promise<boolean>(resolve =>
confirm({
title: `Remove ${getEthClientPrettyName(prevTarget)} volumes?`,
text: `Do you want to keep or remove the volumes of your current Ethereum client? This action cannot be undone.`,
title: `Remove ${getEthClientPrettyName(prevTarget)}?`,
text: `Do you want to keep or remove your current Ethereum client? This action cannot be undone. Having more than one ETH client may cause your machine unexpedted behaviours`,
buttons: [
{
label: "Keep",
variant: "dappnode",
variant: "danger",
onClick: () => resolve(false)
},
{
label: "Remove",
variant: "danger",
variant: "dappnode",
onClick: () => resolve(true)
}
]
Expand All @@ -44,7 +44,7 @@ export const changeEthClientTarget = (
: false;

await withToastNoThrow(
() => api.ethClientTargetSet({ target: nextTarget, deleteVolumes }),
() => api.ethClientTargetSet({ target: nextTarget, deletePrevEthClient }),
{
message: "Changing Eth client...",
onSuccess: `Changed Eth client`
Expand Down
24 changes: 0 additions & 24 deletions packages/dappmanager/src/calls/domainAliasSet.ts

This file was deleted.

6 changes: 3 additions & 3 deletions packages/dappmanager/src/calls/ethClientTargetSet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@ import { EthClientTarget } from "../types";
*/
export async function ethClientTargetSet({
target,
deleteVolumes
deletePrevEthClient
}: {
target: EthClientTarget;
deleteVolumes?: boolean;
deletePrevEthClient?: boolean;
}): Promise<void> {
if (!target) throw Error(`Argument target must be defined`);
if (!ethClientTargets.includes(target))
throw Error(`Unknown client target: ${target}`);

await changeEthMultiClient(target, deleteVolumes);
await changeEthMultiClient(target, deletePrevEthClient);
}
1 change: 0 additions & 1 deletion packages/dappmanager/src/calls/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ export { cleanDb } from "./cleanDb";
export { copyFileTo } from "./copyFileTo";
export { diagnose } from "./diagnose";
export * from "./dockerUpdate";
export { domainAliasSet } from "./domainAliasSet";
export { dappnodeWebNameSet } from "./dappnodeWebNameSet";
export { ethClientTargetSet } from "./ethClientTargetSet";
export { ethClientFallbackSet } from "./ethClientFallbackSet";
Expand Down
6 changes: 5 additions & 1 deletion packages/dappmanager/src/daemons/ethMultiClient/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ import { logs } from "../../logs";
import { EthClientTarget } from "../../types";
import {
EthProviderError,
getLocalFallbackContentHash
getLocalFallbackContentHash,
setDefaultEthClientFullNode
} from "../../modules/ethClient";

/**
Expand Down Expand Up @@ -150,7 +151,10 @@ export function startEthMultiClientDaemon(signal: AbortSignal): void {
if (next.status === "INSTALLED") {
// If status switched to "INSTALLED", map to fullnode.dappnode
// Must be done here in case the package is already installed
// 1. Domain for BIND package
db.fullnodeDomainTarget.set(ethClientData[target].dnpName);
// 2. Add network alias for docker DNS
await setDefaultEthClientFullNode(ethClientData[target].dnpName);
}
}
} catch (e) {
Expand Down
68 changes: 67 additions & 1 deletion packages/dappmanager/src/modules/compose/editor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ import { isNotFoundError } from "../../utils/node";
import { yamlDump, yamlParse } from "../../utils/yaml";
import { ComposeNetwork } from "../../common";

class ComposeServiceEditor {
export class ComposeServiceEditor {
parent: ComposeEditor;
serviceName: string;

Expand Down Expand Up @@ -89,6 +89,72 @@ class ComposeServiceEditor {
}));
}

removeNetworkAliases(
networkName: string,
aliasesToRemove: string[],
serviceNetwork: ComposeServiceNetwork
): void {
this.edit(service => {
const networks = parseServiceNetworks(service.networks || {});
// Network and service network aliases must exist
if (!networks[networkName] || !serviceNetwork.aliases)
throw Error(
"Error removing alias: Network or serviceNetwork does not exist"
);

const serviceNetworNewAliases = serviceNetwork.aliases.filter(
item => !aliasesToRemove.includes(item)
);
const serviceNetworkUpdated = {
...serviceNetwork,
aliases: serviceNetworNewAliases
};

return {
networks: {
...networks,
[networkName]: {
...(networks[networkName] || {}),
...serviceNetworkUpdated
}
}
};
});
}

addNetworkAliases(
networkName: string,
newAliases: string[],
serviceNetwork: ComposeServiceNetwork
): void {
this.edit(service => {
const networks = parseServiceNetworks(service.networks || {});
// Network and service network aliases must exist
if (!networks[networkName] || !serviceNetwork)
throw Error(
"Error adding alias: Network or serviceNetwork does not exist"
);
const aliasesUpdated = uniq([
...(serviceNetwork.aliases || []),
...newAliases
]);
const serviceNetworkUpdated = {
...serviceNetwork,
aliases: aliasesUpdated
};

return {
networks: {
...networks,
[networkName]: {
...(networks[networkName] || {}),
...serviceNetworkUpdated
}
}
};
});
}

getEnvs(): PackageEnvs {
return parseEnvironment(this.get().environment || {});
}
Expand Down
139 changes: 127 additions & 12 deletions packages/dappmanager/src/modules/ethClient/changeEthMultiClient.ts
Original file line number Diff line number Diff line change
@@ -1,38 +1,54 @@
import * as db from "../../db";
import { eventBus } from "../../eventBus";
import { ethClientData } from "../../params";
import { packageRemove } from "../../calls";
import params, { ethClientData } from "../../params";
import { packageRemove, packageGet } from "../../calls";
import { EthClientTarget, UserSettings } from "../../types";
import { logs } from "../../logs";
import { ComposeFileEditor } from "../compose/editor";
import { parseServiceNetworks } from "../compose/networks";
import { dockerNetworkDisconnect, dockerNetworkConnect } from "../docker";
import { getEndpointConfig } from "../https-portal/migration";
import Dockerode from "dockerode";

// Types

/**
* Changes the ethereum client used to fetch package data
* Callable by the client
* @param nextTarget Ethereum client to change to
* @param deleteVolumes If changing from a package client, delete its data
* @param deletePrevEthClient If set delete previous eth1 client
*/
export async function changeEthMultiClient(
nextTarget: EthClientTarget,
deleteVolumes?: boolean,
deletePrevEthClient?: boolean,
userSettings?: UserSettings
): Promise<void> {
const prevTarget = db.ethClientTarget.get();

// Set user settings of next target if any
if (userSettings) db.ethClientUserSettings.set(nextTarget, userSettings);

// If the previous client is a client package, uninstall it
// Delete previous ETH client on demmand by the user
if (prevTarget !== nextTarget && prevTarget && prevTarget !== "remote") {
try {
const clientData = ethClientData[prevTarget];
if (clientData) {
db.ethClientInstallStatus.set(prevTarget, { status: "UNINSTALLED" });
await packageRemove({ dnpName: clientData.dnpName, deleteVolumes });
const clientData = ethClientData[prevTarget];
if (deletePrevEthClient) {
try {
clientData && (await packageRemove({ dnpName: clientData.dnpName }));
// Must await uninstall because geth -> light, light -> geth
// will create conflicts since it's the same DNP
} catch (e) {
logs.error("Error removing previous ETH multi-client", e);
}
} else {
// Remove alias fullnode.dappnode from the eth client if not removed by the user
try {
await setDefaultEthClientFullNode(clientData.dnpName);
} catch (e) {
logs.error(
"Error removing fullnode.dappnode alias from previous ETH multi-client",
e
);
}
} catch (e) {
logs.error("Error removing previous ETH multi-client", e);
}
}

Expand All @@ -43,3 +59,102 @@ export async function changeEthMultiClient(
eventBus.runEthClientInstaller.emit();
}
}

// Utils

export async function setDefaultEthClientFullNode(
dnpName: string
): Promise<void> {
const previousEthClientPackage = await packageGet({
dnpName
});

// Check if ETH client is multiservice, if so get the mainContainer
const mainService = previousEthClientPackage.manifest?.mainService;
const serviceName =
mainService || previousEthClientPackage.containers[0].serviceName;
// The container selected will be:
// - Container owner of the main service (if exists)
// - First container otherwhise
const previousEthClientContainerName =
previousEthClientPackage.containers.find(
container => container.serviceName === serviceName
)?.containerName || previousEthClientPackage.containers[0].containerName;

// Remove fullnode alias from endpoint config
const currentEndpointConfig = await getEndpointConfig(
previousEthClientContainerName
);
const endpointConfig: Partial<Dockerode.NetworkInfo> = {
...currentEndpointConfig,
Aliases: [
...currentEndpointConfig?.Aliases.filter(
// according to docs for compose file v3, aliases are declared as strings https://docs.docker.com/compose/compose-file/compose-file-v3/#aliases
(item: string) => item !== params.FULLNODE_ALIAS
)
]
};
// Remove fullnode alias from compose
removeFullnodeAliasFromCompose(dnpName, serviceName);
await dockerNetworkDisconnect(
params.DNP_PRIVATE_NETWORK_NAME,
previousEthClientContainerName
);
await dockerNetworkConnect(
params.DNP_PRIVATE_NETWORK_NAME,
previousEthClientContainerName,
endpointConfig
);
}

export function removeFullnodeAliasFromCompose(
ethClientDnpName: string,
ethClientServiceName: string
): void {
editComposeFullnodeAliasEthClient(
true,
ethClientDnpName,
ethClientServiceName
);
}

export function addFullnodeAliasToCompose(
ethClientDnpName: string,
ethClientServiceName: string
): void {
editComposeFullnodeAliasEthClient(
false,
ethClientDnpName,
ethClientServiceName
);
}

function editComposeFullnodeAliasEthClient(
removeAlias: boolean,
ethClientDnpName: string,
ethClientServiceName: string
): void {
const compose = new ComposeFileEditor(ethClientDnpName, false);

const composeService = compose.services()[ethClientServiceName];
const serviceNetworks = parseServiceNetworks(
composeService.get().networks || {}
);
const serviceNetwork =
serviceNetworks[params.DNP_PRIVATE_NETWORK_NAME] ?? null;

if (removeAlias)
composeService.removeNetworkAliases(
params.DNP_PRIVATE_NETWORK_NAME,
[params.FULLNODE_ALIAS],
serviceNetwork
);
else
composeService.addNetworkAliases(
params.DNP_PRIVATE_NETWORK_NAME,
[params.FULLNODE_ALIAS],
serviceNetwork
);

compose.write();
}
Loading

0 comments on commit 0487a70

Please sign in to comment.