Skip to content

Commit

Permalink
Merge pull request #1005 from dappnode/3alpha/https-fix
Browse files Browse the repository at this point in the history
Check for rest of the packages for HTTPS portal
  • Loading branch information
pablomendezroyo authored Jun 1, 2022
2 parents 580fb7a + 67e4da7 commit 552ab6c
Show file tree
Hide file tree
Showing 3 changed files with 179 additions and 32 deletions.
30 changes: 24 additions & 6 deletions packages/dappmanager/src/modules/https-portal/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,14 +69,18 @@ export class HttpsPortal {
// Edit compose to persist the setting
addNetworkAliasCompose(container, externalNetworkName, aliases);


// Check whether DNP_HTTPS compose has external network persisted
const httpsComposePath = ComposeEditor.getComposePath(params.HTTPS_PORTAL_DNPNAME, true)
const editor = new ComposeEditor(ComposeEditor.readFrom(httpsComposePath))
const httpsComposePath = ComposeEditor.getComposePath(
params.HTTPS_PORTAL_DNPNAME,
true
);
const editor = new ComposeEditor(ComposeEditor.readFrom(httpsComposePath));

if(editor.getComposeNetwork(externalNetworkName) === null) {
const httpsExternalAlias = getExternalNetworkAlias(httpsPortalContainer)
addNetworkAliasCompose(httpsPortalContainer, externalNetworkName, [httpsExternalAlias])
if (editor.getComposeNetwork(externalNetworkName) === null) {
const httpsExternalAlias = getExternalNetworkAlias(httpsPortalContainer);
addNetworkAliasCompose(httpsPortalContainer, externalNetworkName, [
httpsExternalAlias
]);
}
}

Expand Down Expand Up @@ -146,6 +150,20 @@ export class HttpsPortal {
return mappings;
}

/**
* Returns true if the container has assigned a mapping to the https-portal
*/
async hasMapping(dnpName: string, serviceName: string): Promise<boolean> {
const entries = await this.httpsPortalApiClient.list();
const mappingAlias = getExternalNetworkAlias({ serviceName, dnpName });
for (const { toHost } of entries) {
// toHost format: someDomain:80
const alias = toHost.split(":")[0];
if (alias === mappingAlias) return true;
}
return false;
}

private async getContainerForMapping(
mapping: HttpsPortalMapping,
containers?: PackageContainer[]
Expand Down
149 changes: 149 additions & 0 deletions packages/dappmanager/src/modules/installer/https.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
import { listPackageNoThrow } from "../docker/list/listPackages";
import { httpsPortal } from "../../calls/httpsPortal";
import { prettyDnpName } from "../../utils/format";
import params from "../../params";
import { InstallPackageData } from "../../types";
import { Log } from "../../utils/logUi";
import { HttpsPortalMapping } from "../../common";
import { getExternalNetworkAlias } from "../../domains";
import {
dockerListNetworks,
dockerCreateNetwork,
dockerNetworkConnect
} from "../docker";

/**
* Connect to dnpublic_network with an alias if:
* - is HTTPS package
* - any package with https portal mappings
*/
export async function connectToPublicNetwork(
pkg: InstallPackageData,
externalNetworkName: string
): Promise<void> {
// if there is no https, checks aren't needed
if (!(await isRunningHttps())) return;

// create network if necessary
const networks = await dockerListNetworks();
if (!networks.find(network => network.Name === externalNetworkName))
await dockerCreateNetwork(externalNetworkName);

const containers =
(
await listPackageNoThrow({
dnpName: pkg.dnpName
})
)?.containers || [];

if (containers.length === 0) return;

for (const container of containers) {
if (
pkg.dnpName === params.HTTPS_PORTAL_DNPNAME ||
(await httpsPortal.hasMapping(pkg.dnpName, container.serviceName))
) {
const alias = getExternalNetworkAlias({
serviceName: container.serviceName,
dnpName: pkg.dnpName
});

if (!container.networks.find(n => n.name === externalNetworkName)) {
await dockerNetworkConnect(
externalNetworkName,
container.containerName,
{ Aliases: [alias] }
);
}
}
}
}

/**
* Expose default HTTPS ports on installation defined in the manifest - exposable
*/
export async function exposeByDefaultHttpsPorts(
pkg: InstallPackageData,
log: Log
): Promise<void> {
if (pkg.metadata.exposable) {
// Requires that https package exists and it is running
if (!(await isRunningHttps()))
throw Error(
`HTTPS package not running but required to expose HTTPS ports by default.`
);

const currentMappings = await httpsPortal.getMappings();
const portMappinRollback: HttpsPortalMapping[] = [];

for (const exposable of pkg.metadata.exposable) {
if (exposable.exposeByDefault) {
const portalMapping: HttpsPortalMapping = {
fromSubdomain: exposable.fromSubdomain || prettyDnpName(pkg.dnpName), // get dnpName by default
dnpName: pkg.dnpName,
serviceName:
exposable.serviceName || Object.keys(pkg.compose.services)[0], // get first service name by default (docs: https://docs.dappnode.io/es/developers/manifest-reference/#servicename)
port: exposable.port
};

if (
currentMappings.length > 0 &&
currentMappings.includes(portalMapping)
)
continue;

try {
// Expose default HTTPS ports
log(
pkg.dnpName,
`Exposing ${prettyDnpName(pkg.dnpName)}:${
exposable.port
} to the external internet`
);
await httpsPortal.addMapping(portalMapping);
portMappinRollback.push(portalMapping);

log(
pkg.dnpName,
`Exposed ${prettyDnpName(pkg.dnpName)}:${
exposable.port
} to the external internet`
);
} catch (e) {
e.message = `${e.message} Error exposing default HTTPS ports, removing mappings`;
for (const mappingRollback of portMappinRollback) {
await httpsPortal.removeMapping(mappingRollback).catch(e => {
log(
pkg.dnpName,
`Error removing mapping ${JSON.stringify(mappingRollback)}, ${
e.message
}`
);
});
}
throw e;
}
}
}
}
}

// Utils

/**
* Returns true if HTTPS package installed and running, otherwise return false
*/
async function isRunningHttps() {
const httpsPackage = await listPackageNoThrow({
dnpName: params.HTTPS_PORTAL_DNPNAME
});

if (!httpsPackage) return false;

// Check every HTTPS container is running
httpsPackage.containers.forEach(container => {
if (!container.running) return false;
});

return true;
}
32 changes: 6 additions & 26 deletions packages/dappmanager/src/modules/installer/runPackages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,11 @@ import { Log } from "../../utils/logUi";
import { copyFileTo } from "../../calls/copyFileTo";
import { InstallPackageData } from "../../types";
import { logs } from "../../logs";
import { dockerComposeUpPackage, dockerCreateNetwork, dockerListNetworks } from "../docker";
import { dockerComposeUpPackage } from "../docker";
import { packageToInstallHasPid } from "../../utils/pid";
import { exposeByDefaultHttpsPorts } from "./exposeByDefaultHttpsPorts";
import { ComposeFileEditor } from "../compose/editor";
import { connectToPublicNetwork, exposeByDefaultHttpsPorts } from "./https";

const externalNetworkName = params.DNP_EXTERNAL_NETWORK_NAME;

/**
* Create and run each package container in series
Expand Down Expand Up @@ -40,29 +41,6 @@ export async function runPackages(
// - Allow copying files without duplicating logic
// - Allow conditionally starting containers latter if were previously running
log(pkg.dnpName, "Preparing package...");


// Recreate HTTPs portal mapping if installing or updating HTTPs package
if (pkg.dnpName === params.HTTPS_PORTAL_DNPNAME) {
log(pkg.dnpName, "Ensuring HTTPS network exists...");

const networks = await dockerListNetworks();
const externalNetworkName = params.DNP_EXTERNAL_NETWORK_NAME;
if (!networks.find(network => network.Name === externalNetworkName)) {
await dockerCreateNetwork(externalNetworkName);
}

// Check whether DNP_HTTPS compose has external network persisted
const compose = new ComposeFileEditor(pkg.dnpName, true);
if(compose.getComposeNetwork(externalNetworkName) === null) {
log(pkg.dnpName, "Adding external network to HTTPS compose...");
const composeService = compose.services()["https.dnp.dappnode.eth"];
const aliases: string[] = ["https.external"];
composeService.addNetwork(externalNetworkName, {aliases});
compose.write();
}
}

await dockerComposeUp(pkg.composePath, {
// To clean-up changing multi-service packages, remove orphans
// but NOT for core packages, which always have orphans
Expand Down Expand Up @@ -104,6 +82,8 @@ export async function runPackages(
log(pkg.dnpName, "Package started");

// Expose default HTTPs ports if required

await connectToPublicNetwork(pkg, externalNetworkName);
await exposeByDefaultHttpsPorts(pkg, log);
}
}

0 comments on commit 552ab6c

Please sign in to comment.