Skip to content

Commit

Permalink
Implement docker upgrade (#1847)
Browse files Browse the repository at this point in the history
* Implement docker upgrade

* fix mock

* fix parsing

* redirect output

* Add messages

* fix get docker latest version

* update text

* check for docker latest version existance

* check for value and return undefined if empty string

* parse string to boolean

* remove unnecesary alert

* Improve messaging

* update requirements after upgrade

* remove consoles
  • Loading branch information
pablomendezroyo authored Feb 27, 2024
1 parent 9c2a2b3 commit cca7660
Show file tree
Hide file tree
Showing 17 changed files with 494 additions and 3 deletions.
7 changes: 7 additions & 0 deletions packages/admin-ui/src/__mock-backend__/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -379,6 +379,13 @@ export const otherCalls: Omit<Routes, keyof typeof namedSpacedCalls> = {
}),
optimismConfigSet: async () => {},
updateUpgrade: async () => "Successfully updated",
dockerUpgrade: async () => {},
dockerUpgradeCheck: async () => ({
isDockerInUnattendedUpgrades: true,
isDockerInstalledThroughApt: true,
dockerHostVersion: "20.10.7",
dockerLatestVersion: "20.10.8"
}),
getIsConnectedToInternet: async () => false
};

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
import React, { useState, useEffect } from "react";
import { ReqStatus } from "types";
import { api } from "api";
import { confirm } from "components/ConfirmDialog";
import Button from "components/Button";
import Ok from "components/Ok";
import ErrorView from "components/ErrorView";
import { withToast } from "components/toast/Toast";
import { DockerUpgradeRequirements } from "@dappnode/types";
import { gte, lt } from "semver";
import Card from "components/Card";

function RequirementsList({ items }: { items: DockerUpgradeRequirements }) {
return (
<div>
<Ok
title="Docker in unattended upgrades"
msg={
items.isDockerInUnattendedUpgrades
? `Docker is in the unattended upgrades list. This means that it will be automatically updated by the system`
: `Docker is not in the unattended upgrades list. This means that it will not be automatically updated by the system`
}
ok={items.isDockerInUnattendedUpgrades}
/>
<Ok
title="Docker installed through apt"
msg={
items.isDockerInstalledThroughApt
? `Docker has been installed through the apt package manager. This is the recommended way to install docker in DAppNode so it can be automatically updated by unattended upgrades`
: `Docker has not been installed through the apt package manager. This means that it will not be automatically updated by unattended upgrades`
}
ok={items.isDockerInstalledThroughApt}
/>
<Ok
title="Docker is updated"
msg={
items.isDockerInstalledThroughApt
? `Docker host version ${items.dockerHostVersion}, Docker latest version ${items.dockerLatestVersion}`
: `Docker host version ${items.dockerHostVersion}. Could not be determined the latest docker version available because docker is not installed through apt`
}
ok={
items.isDockerInstalledThroughApt && items.dockerLatestVersion
? gte(items.dockerHostVersion, items.dockerLatestVersion)
: false
}
/>
</div>
);
}

export function DockerUpgrade() {
const [updateReq, setUpdateReq] = useState<ReqStatus>({});
const [checkReq, setCheckReq] = useState<
ReqStatus<DockerUpgradeRequirements>
>({});
const [canUpdate, setCanUpdate] = useState<boolean>(false);

useEffect(() => {
if (checkReq.result) {
const {
isDockerInUnattendedUpgrades,
isDockerInstalledThroughApt,
dockerHostVersion,
dockerLatestVersion
} = checkReq.result;
const canUpdate =
!isDockerInUnattendedUpgrades ||
!isDockerInstalledThroughApt ||
Boolean(
dockerLatestVersion && lt(dockerHostVersion, dockerLatestVersion)
);
setCanUpdate(canUpdate);
}
}, [checkReq.result]);

async function dockerUpdateCheck() {
try {
setCheckReq({ loading: true });
const requirements = await api.dockerUpgradeCheck();
setCheckReq({ result: requirements });
} catch (e) {
setCheckReq({ error: e });
}
}

async function dockerUpdate() {
try {
await new Promise<void>(resolve => {
confirm({
title: `Docker update`,
text: `Warning, you are about to update Docker . It is possible that the system will need to reboot. Make sure you can sustain some minutes of downtime and backup your most important data.`,
label: "Update",
onClick: resolve
});
});

setUpdateReq({ loading: true });
await withToast(() => api.dockerUpgrade(), {
message: "Updating Docker",
onSuccess: "Updated Docker"
});
setUpdateReq({ result: true });
// update the requirements after the update
await dockerUpdateCheck();
} catch (e) {
setUpdateReq({ error: e });
}
}

return (
<Card spacing>
<p>
Update docker to latest version, make sure its installed through the
standard apt package manager and enable unattended upgrades for docker
so you do not worry about updating docker anymore
</p>

{!canUpdate && (
<Button disabled={checkReq.loading} onClick={dockerUpdateCheck}>
Check update requirements
</Button>
)}

{checkReq.error ? (
<ErrorView error={checkReq.error} red hideIcon />
) : checkReq.loading ? (
<Ok msg={"Checking update requirements..."} loading={true} />
) : null}

{checkReq.result && (
<>
<RequirementsList items={checkReq.result} />

{canUpdate && (
<Button disabled={updateReq.loading} onClick={dockerUpdate}>
Update docker
</Button>
)}

{updateReq.result ? (
<Ok ok={true} msg={"Successfully updated docker"} />
) : updateReq.loading ? (
<Ok loading={true} msg={"Updating docker"} />
) : updateReq.error ? (
<ErrorView error={updateReq.error} red hideIcon />
) : null}
</>
)}
</Card>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import Button from "components/Button";
import { confirm } from "components/ConfirmDialog";
import { withToastNoThrow } from "components/toast/Toast";

export default function UpdateUpgrade() {
export function UpdateUpgrade() {
async function updateUpgrade() {
await new Promise<void>(resolve =>
confirm({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ import { SshManager } from "./SshManager";
import { ClearCacheDb } from "./ClearCacheDb";
import { ClearMainDb } from "./ClearMainDb";
import { ReleaseTrustedKeysEditor } from "./ReleaseTrustedKeysEditor";
import UpdateUpgrade from "./UpdateUpgrade";
import { UpdateUpgrade } from "./UpdateUpgrade";
import { DockerUpgrade } from "./DockerUpgrade";

export function Advanced() {
return (
Expand All @@ -29,6 +30,9 @@ export function Advanced() {
<SubTitle>Update and upgrade the host machine</SubTitle>
<UpdateUpgrade />

<SubTitle>Docker update</SubTitle>
<DockerUpgrade />

<SubTitle>Clear cache db</SubTitle>
<ClearCacheDb />

Expand Down
19 changes: 19 additions & 0 deletions packages/dappmanager/src/calls/dockerUpgrade.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import {
dockerUpgradeService,
dockerUpgradeCheck as _dockerUpgradeCheck
} from "@dappnode/hostscriptsservices";
import { DockerUpgradeRequirements } from "@dappnode/types";

/**
* Updates docker engine
*/
export async function dockerUpgrade(): Promise<void> {
await dockerUpgradeService();
}

/**
* Checks requirements to update docker
*/
export async function dockerUpgradeCheck(): Promise<DockerUpgradeRequirements> {
return await _dockerUpgradeCheck();
}
1 change: 1 addition & 0 deletions packages/dappmanager/src/calls/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export { cleanDb } from "./cleanDb.js";
export { stakerConfigSet, stakerConfigGet } from "./stakerConfig.js";
export { copyFileToDockerContainer } from "./copyFileToDockerContainer.js";
export { diagnose } from "./diagnose.js";
export { dockerUpgradeCheck, dockerUpgrade } from "./dockerUpgrade.js";
export { dappnodeWebNameSet } from "./dappnodeWebNameSet.js";
export { ethClientTargetSet } from "./ethClientTargetSet.js";
export { ethClientFallbackSet } from "./ethClientFallbackSet.js";
Expand Down
Loading

0 comments on commit cca7660

Please sign in to comment.