diff --git a/packages/admin-ui/src/__mock-backend__/index.ts b/packages/admin-ui/src/__mock-backend__/index.ts index b092ca9ba..4791705d5 100644 --- a/packages/admin-ui/src/__mock-backend__/index.ts +++ b/packages/admin-ui/src/__mock-backend__/index.ts @@ -63,7 +63,6 @@ export const otherCalls: Omit = { cleanDb: async () => {}, copyFileTo: async () => {}, diagnose: async () => [], - domainAliasSet: async () => {}, ethClientFallbackSet: async () => {}, ethClientTargetSet: async () => {}, ipfsTest: async () => {}, diff --git a/packages/admin-ui/src/common/routes.ts b/packages/admin-ui/src/common/routes.ts index 6f776f4f9..551e97a31 100644 --- a/packages/admin-ui/src/common/routes.ts +++ b/packages/admin-ui/src/common/routes.ts @@ -194,11 +194,6 @@ export interface Routes { /** Returns docker engine update requirements */ dockerEngineUpdateCheck: () => Promise; - /** - * Set a domain alias to a DAppNode package by name - */ - domainAliasSet: (kwargs: { alias: string; dnpName: string }) => Promise; - /** * Sets if a fallback should be used */ @@ -211,7 +206,7 @@ export interface Routes { */ ethClientTargetSet: (kwargs: { target: EthClientTarget; - deleteVolumes?: boolean; + deletePrevEthClient?: boolean; }) => Promise; /** @@ -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: {}, diff --git a/packages/admin-ui/src/pages/system/actions.ts b/packages/admin-ui/src/pages/system/actions.ts index 7ec99bf9f..665b1d370 100644 --- a/packages/admin-ui/src/pages/system/actions.ts +++ b/packages/admin-ui/src/pages/system/actions.ts @@ -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(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) } ] @@ -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` diff --git a/packages/admin-ui/src/pages/system/components/Repository.tsx b/packages/admin-ui/src/pages/system/components/Repository/index.tsx similarity index 100% rename from packages/admin-ui/src/pages/system/components/Repository.tsx rename to packages/admin-ui/src/pages/system/components/Repository/index.tsx diff --git a/packages/dappmanager/src/calls/domainAliasSet.ts b/packages/dappmanager/src/calls/domainAliasSet.ts deleted file mode 100644 index e14d7e2c2..000000000 --- a/packages/dappmanager/src/calls/domainAliasSet.ts +++ /dev/null @@ -1,24 +0,0 @@ -import * as db from "../db"; -import { eventBus } from "../eventBus"; - -/** - * Set a domain alias to a DAppNode package by name - */ -export async function domainAliasSet({ - alias, - dnpName -}: { - alias: string; - dnpName: string; -}): Promise { - switch (alias) { - case "fullnode": - db.fullnodeDomainTarget.set(dnpName); - break; - default: - throw Error(`Unknown alias: ${alias}`); - } - - // Trigger an nsupdate run - eventBus.packagesModified.emit({ dnpNames: [dnpName] }); -} diff --git a/packages/dappmanager/src/calls/ethClientTargetSet.ts b/packages/dappmanager/src/calls/ethClientTargetSet.ts index b4f6f6112..e442d4d4d 100644 --- a/packages/dappmanager/src/calls/ethClientTargetSet.ts +++ b/packages/dappmanager/src/calls/ethClientTargetSet.ts @@ -7,14 +7,14 @@ import { EthClientTarget } from "../types"; */ export async function ethClientTargetSet({ target, - deleteVolumes + deletePrevEthClient }: { target: EthClientTarget; - deleteVolumes?: boolean; + deletePrevEthClient?: boolean; }): Promise { 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); } diff --git a/packages/dappmanager/src/calls/index.ts b/packages/dappmanager/src/calls/index.ts index 36f09cd7c..8e26f9ae5 100644 --- a/packages/dappmanager/src/calls/index.ts +++ b/packages/dappmanager/src/calls/index.ts @@ -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"; diff --git a/packages/dappmanager/src/daemons/ethMultiClient/index.ts b/packages/dappmanager/src/daemons/ethMultiClient/index.ts index 3c84add61..da8310aac 100644 --- a/packages/dappmanager/src/daemons/ethMultiClient/index.ts +++ b/packages/dappmanager/src/daemons/ethMultiClient/index.ts @@ -15,7 +15,8 @@ import { logs } from "../../logs"; import { EthClientTarget } from "../../types"; import { EthProviderError, - getLocalFallbackContentHash + getLocalFallbackContentHash, + setDefaultEthClientFullNode } from "../../modules/ethClient"; /** @@ -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) { diff --git a/packages/dappmanager/src/modules/compose/editor.ts b/packages/dappmanager/src/modules/compose/editor.ts index 07c70f241..a3c9ad4f2 100644 --- a/packages/dappmanager/src/modules/compose/editor.ts +++ b/packages/dappmanager/src/modules/compose/editor.ts @@ -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; @@ -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 || {}); } diff --git a/packages/dappmanager/src/modules/ethClient/changeEthMultiClient.ts b/packages/dappmanager/src/modules/ethClient/changeEthMultiClient.ts index 5eca5d757..c5ead8cbb 100644 --- a/packages/dappmanager/src/modules/ethClient/changeEthMultiClient.ts +++ b/packages/dappmanager/src/modules/ethClient/changeEthMultiClient.ts @@ -1,19 +1,26 @@ 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 { const prevTarget = db.ethClientTarget.get(); @@ -21,18 +28,27 @@ export async function changeEthMultiClient( // 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); } } @@ -43,3 +59,102 @@ export async function changeEthMultiClient( eventBus.runEthClientInstaller.emit(); } } + +// Utils + +export async function setDefaultEthClientFullNode( + dnpName: string +): Promise { + 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 = { + ...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(); +} diff --git a/packages/dappmanager/src/modules/https-portal/migration.ts b/packages/dappmanager/src/modules/https-portal/migration.ts index 832ff980e..23cbca35d 100644 --- a/packages/dappmanager/src/modules/https-portal/migration.ts +++ b/packages/dappmanager/src/modules/https-portal/migration.ts @@ -83,7 +83,7 @@ export function hasAlias( } /** Get endpoint config for DNP_PRIVATE_NETWORK_NAME */ -async function getEndpointConfig( +export async function getEndpointConfig( containerName: string ): Promise { const inspectInfo = await dockerContainerInspect(containerName); diff --git a/packages/dappmanager/src/params.ts b/packages/dappmanager/src/params.ts index 5213fbd36..016351b13 100644 --- a/packages/dappmanager/src/params.ts +++ b/packages/dappmanager/src/params.ts @@ -234,6 +234,8 @@ const params = { "geth.dnp.dappnode.eth", "parity.dnp.dappnode.eth" ], + // Default fullnode alias + FULLNODE_ALIAS: "fullnode.dappnode", // ETHFORWARD / HTTP proxy params ETHFORWARD_IPFS_REDIRECT: "http://ipfs.dappnode:8080", diff --git a/packages/dappmanager/test/modules/compose/editor/composeAliases.test.ts b/packages/dappmanager/test/modules/compose/editor/composeAliases.test.ts new file mode 100644 index 000000000..24f938389 --- /dev/null +++ b/packages/dappmanager/test/modules/compose/editor/composeAliases.test.ts @@ -0,0 +1,209 @@ +import "mocha"; +import { expect } from "chai"; +import { + ComposeFileEditor, + ComposeServiceEditor +} from "../../../../src/modules/compose/editor"; +import params from "../../../../src/params"; +import { shellSafe } from "../../../testUtils"; +import fs from "fs"; +import { parseServiceNetworks } from "../../../../src/modules/compose/networks"; +import { ComposeServiceNetwork } from "../../../../src/types"; + +describe("compose service editor", () => { + const exampleCompose = ` +version: '3.5' +services: + goerli-geth.dnp.dappnode.eth: + container_name: DAppNodePackage-goerli-geth.dnp.dappnode.eth + dns: 172.33.1.2 + environment: + - 'EXTRA_OPTIONS=--http.api eth,net,web3,txpool' + image: 'goerli-geth.dnp.dappnode.eth:0.4.12' + logging: + driver: json-file + options: + max-size: 10m + max-file: '3' + networks: + dncore_network: + aliases: + - goerli-geth.dappnode + ports: + - '30303' + - 30303/udp + - 30304/udp + restart: always + volumes: + - 'goerli:/goerli' + labels: + dappnode.dnp.dnpName: goerli-geth.dnp.dappnode.eth + dappnode.dnp.version: 0.4.12 +volumes: + goerli: {} +networks: + dncore_network: + external: true +`; + + // Example package + const dnpName = "example"; + const serviceName = "goerli-geth.dnp.dappnode.eth"; + const dnpRepoExamplePath = process.cwd() + "/dnp_repo/example"; + + before("Create random compose to be edited", async () => { + // Create necessary dir + await shellSafe(`mkdir ${dnpRepoExamplePath}`); + // Create example compose + fs.writeFileSync( + `${dnpRepoExamplePath}/docker-compose.yml`, + exampleCompose + ); + }); + + it("Should remove alias: example.dappnode", () => { + const { compose, composeService, serviceNetwork } = { + ...getComposeEditors(dnpName, serviceName) + }; + // Edit existing compose + composeService.removeNetworkAliases( + params.DNP_PRIVATE_NETWORK_NAME, + ["goerli-geth.dappnode"], + serviceNetwork + ); + compose.write(); + + // Get edited compose + const composeAfter = fs.readFileSync( + `${dnpRepoExamplePath}/docker-compose.yml`, + "utf-8" + ); + + const composeExpected = ` +version: '3.5' +services: + goerli-geth.dnp.dappnode.eth: + container_name: DAppNodePackage-goerli-geth.dnp.dappnode.eth + dns: 172.33.1.2 + environment: + - 'EXTRA_OPTIONS=--http.api eth,net,web3,txpool' + image: 'goerli-geth.dnp.dappnode.eth:0.4.12' + logging: + driver: json-file + options: + max-size: 10m + max-file: '3' + networks: + dncore_network: + aliases: [] + ports: + - '30303' + - 30303/udp + - 30304/udp + restart: always + volumes: + - 'goerli:/goerli' + labels: + dappnode.dnp.dnpName: goerli-geth.dnp.dappnode.eth + dappnode.dnp.version: 0.4.12 +volumes: + goerli: {} +networks: + dncore_network: + external: true +`; + + expect(composeAfter.trim()).to.equal(composeExpected.trim()); + }); + + it("Should add alias: goerli-geth.dappnode", () => { + const { compose, composeService, serviceNetwork } = { + ...getComposeEditors(dnpName, serviceName) + }; + // Edit existing compose + composeService.addNetworkAliases( + params.DNP_PRIVATE_NETWORK_NAME, + ["example.dappnode"], + serviceNetwork + ); + compose.write(); + + // Get edited compose + const composeAfter = fs.readFileSync( + `${dnpRepoExamplePath}/docker-compose.yml`, + "utf-8" + ); + + const composeExpected = ` +version: '3.5' +services: + goerli-geth.dnp.dappnode.eth: + container_name: DAppNodePackage-goerli-geth.dnp.dappnode.eth + dns: 172.33.1.2 + environment: + - 'EXTRA_OPTIONS=--http.api eth,net,web3,txpool' + image: 'goerli-geth.dnp.dappnode.eth:0.4.12' + logging: + driver: json-file + options: + max-size: 10m + max-file: '3' + networks: + dncore_network: + aliases: + - goerli-geth.dappnode + - example.dappnode + ports: + - '30303' + - 30303/udp + - 30304/udp + restart: always + volumes: + - 'goerli:/goerli' + labels: + dappnode.dnp.dnpName: goerli-geth.dnp.dappnode.eth + dappnode.dnp.version: 0.4.12 +volumes: + goerli: {} +networks: + dncore_network: + external: true +`; + expect(composeAfter.trim()).to.equal(composeExpected.trim()); + }); + + afterEach(() => { + // Overwrite the compose to be used in more tests + fs.writeFileSync( + `${dnpRepoExamplePath}/docker-compose.yml`, + exampleCompose + ); + }); + + after("Remove setup", async () => { + await shellSafe(`rm -rf ${dnpRepoExamplePath}`); + }); +}); + +function getComposeEditors( + dnpName: string, + serviceName: string +): { + compose: ComposeFileEditor; + composeService: ComposeServiceEditor; + serviceNetwork: ComposeServiceNetwork; +} { + // Create compose editors + const compose = new ComposeFileEditor(dnpName, false); + const composeService = compose.services()[serviceName]; + const serviceNetworks = parseServiceNetworks( + composeService.get().networks || {} + ); + const serviceNetwork = + serviceNetworks[params.DNP_PRIVATE_NETWORK_NAME] ?? null; + return { + compose, + composeService, + serviceNetwork + }; +} diff --git a/packages/dappmanager/test/modules/ethClient/fullNodeEdit.test.ts b/packages/dappmanager/test/modules/ethClient/fullNodeEdit.test.ts new file mode 100644 index 000000000..8b2782570 --- /dev/null +++ b/packages/dappmanager/test/modules/ethClient/fullNodeEdit.test.ts @@ -0,0 +1,130 @@ +import "mocha"; +import { expect } from "chai"; +import { shellSafe } from "../../testUtils"; +import fs from "fs"; +import { + removeFullnodeAliasFromCompose, + addFullnodeAliasToCompose +} from "../../../src/modules/ethClient/changeEthMultiClient"; + +// The following test will wite a compose with the alias fullnode.dappnode: +// 1. Then will remove such aslias and test it +// 2. With the existing edited compose will add back again the alias: fullnode.dappnode and test it + +describe("Edit fullnode in eth client", () => { + const composeWithFullnodeAlias = ` +version: '3.5' +services: + goerli-geth.dnp.dappnode.eth: + container_name: DAppNodePackage-goerli-geth.dnp.dappnode.eth + dns: 172.33.1.2 + environment: + - 'EXTRA_OPTIONS=--http.api eth,net,web3,txpool' + image: 'goerli-geth.dnp.dappnode.eth:0.4.12' + logging: + driver: json-file + options: + max-size: 10m + max-file: '3' + networks: + dncore_network: + aliases: + - goerli-geth.dappnode + - fullnode.dappnode + ports: + - '30303' + - 30303/udp + - 30304/udp + restart: always + volumes: + - 'goerli:/goerli' + labels: + dappnode.dnp.dnpName: goerli-geth.dnp.dappnode.eth + dappnode.dnp.version: 0.4.12 +volumes: + goerli: {} +networks: + dncore_network: + external: true +`; + + const composeWithOutFullnodeAlias = ` +version: '3.5' +services: + goerli-geth.dnp.dappnode.eth: + container_name: DAppNodePackage-goerli-geth.dnp.dappnode.eth + dns: 172.33.1.2 + environment: + - 'EXTRA_OPTIONS=--http.api eth,net,web3,txpool' + image: 'goerli-geth.dnp.dappnode.eth:0.4.12' + logging: + driver: json-file + options: + max-size: 10m + max-file: '3' + networks: + dncore_network: + aliases: + - goerli-geth.dappnode + ports: + - '30303' + - 30303/udp + - 30304/udp + restart: always + volumes: + - 'goerli:/goerli' + labels: + dappnode.dnp.dnpName: goerli-geth.dnp.dappnode.eth + dappnode.dnp.version: 0.4.12 +volumes: + goerli: {} +networks: + dncore_network: + external: true +`; + + // Example package + const dnpName = "example"; + const serviceName = "goerli-geth.dnp.dappnode.eth"; + const dnpRepoExamplePath = process.cwd() + "/dnp_repo/example"; + + before("Create random compose to be edited", async () => { + // Create necessary dir + await shellSafe(`mkdir ${dnpRepoExamplePath}`); + // Create example compose with fullnode + fs.writeFileSync( + `${dnpRepoExamplePath}/docker-compose.yml`, + composeWithFullnodeAlias + ); + }); + + it("Should remove alias: fullnode.dappnode", () => { + // Edit existing compose + removeFullnodeAliasFromCompose(dnpName, serviceName); + + // Get edited compose + const composeAfter = fs.readFileSync( + `${dnpRepoExamplePath}/docker-compose.yml`, + "utf-8" + ); + + expect(composeAfter.trim()).to.equal(composeWithOutFullnodeAlias.trim()); + }); + + it("Should add alias: fullnode.dappnode", () => { + // Edit existing compose + addFullnodeAliasToCompose(dnpName, serviceName); + + // Get edited compose + const composeAfter = fs.readFileSync( + `${dnpRepoExamplePath}/docker-compose.yml`, + "utf-8" + ); + + expect(composeAfter.trim()).to.equal(composeWithFullnodeAlias.trim()); + }); + + after("Remove setup", async () => { + await shellSafe(`rm -rf ${dnpRepoExamplePath}`); + }); +});