diff --git a/deps/candid/rdmx6-jaaaa-aaaaa-aaadq-cai.did b/deps/candid/rdmx6-jaaaa-aaaaa-aaadq-cai.did index f8aa40a..0164427 100644 --- a/deps/candid/rdmx6-jaaaa-aaaaa-aaadq-cai.did +++ b/deps/candid/rdmx6-jaaaa-aaaaa-aaadq-cai.did @@ -542,11 +542,6 @@ service : (opt InternetIdentityInit) -> { fetch_entries: () -> (vec BufferedArchiveEntry); acknowledge_entries: (sequence_number: nat64) -> (); - // Calls used for event stats housekeeping. - // Only callable by the canister itself. - prune_events_if_necessary: () -> (); - inject_prune_event: (timestamp: Timestamp) -> (); - // V2 API // WARNING: The following methods are experimental and may change in the future. diff --git a/deps/pulled.json b/deps/pulled.json index 9e2d061..c832cba 100644 --- a/deps/pulled.json +++ b/deps/pulled.json @@ -2,12 +2,12 @@ "canisters": { "rdmx6-jaaaa-aaaaa-aaadq-cai": { "name": "internet_identity", - "wasm_hash": "764cff569a98a3c4d54cba6750fda63f554fc53e7d42a6365d9bdec3280d63c3", - "wasm_hash_download": "764cff569a98a3c4d54cba6750fda63f554fc53e7d42a6365d9bdec3280d63c3", + "wasm_hash": "01797eac7db02126e7cb507e2f3134e2d7b5154289808fdc2c8c03d1fc2dc60e", + "wasm_hash_download": "01797eac7db02126e7cb507e2f3134e2d7b5154289808fdc2c8c03d1fc2dc60e", "init_guide": "Use '(null)' for sensible defaults. See the candid interface for more details.", "init_arg": null, "candid_args": "(opt InternetIdentityInit)", "gzip": true } } -} +} \ No newline at end of file diff --git a/frontend/app/dashboard/new-deployment/page.tsx b/frontend/app/dashboard/new-deployment/page.tsx index 930851a..04c574f 100644 --- a/frontend/app/dashboard/new-deployment/page.tsx +++ b/frontend/app/dashboard/new-deployment/page.tsx @@ -10,9 +10,7 @@ import { useIcContext, } from "@/contexts/IcContext"; import type { DeploymentParams, DeploymentState } from "@/declarations/backend.did"; -import { extractDeploymentCreated } from "@/helpers/deployment"; import { extractOk } from "@/helpers/result"; -import { sendManifestToProvider } from "@/services/deployment"; import { useRouter } from "next/navigation"; import { useCallback, useEffect, useMemo, useState } from "react"; import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"; @@ -21,6 +19,7 @@ import { displayE8sAsIcp, icpToE8s } from "@/helpers/ui"; import { Spinner } from "@/components/spinner"; import { NewDeploymentForm } from "@/components/new-deployment-form"; import { transferE8sToBackend } from "@/services/backend"; +import { completeDeployment, confirmDeployment, updateDeploymentState } from "@/services/deployment"; const FETCH_DEPLOYMENT_PRICE_INTERVAL_MS = 30_000; // 30 seconds @@ -98,6 +97,26 @@ export default function NewDeployment() { } }, [backendActor, deploymentE8sPrice, ledgerCanister, refreshLedgerData, toastError]); + const setDeploymentAsActive = useCallback(async (deploymentId: string) => { + try { + closeWs(); + + const stepActive = { + Active: null, + }; + setDeploymentSteps((prev) => [...prev, stepActive]); + + await completeDeployment(backendActor!, deploymentId); + + setIsDeploying(false); + router.push("/dashboard"); + } catch (e) { + console.error("Failed to complete deployment:", e); + setDeploymentError("Failed to complete deployment, see console for details"); + setIsDeploying(false); + } + }, [backendActor, closeWs, router]); + const onWsOpen: OnWsOpenCallback = useCallback(async () => { console.log("ws open"); @@ -153,29 +172,21 @@ export default function NewDeployment() { return; } - let leaseCreated = false; - try { if ("LeaseCreated" in deploymentUpdate.update) { - const { manifest_sorted_json, dseq } = extractDeploymentCreated( - deploymentSteps.find((el) => - el.hasOwnProperty("DeploymentCreated") - )! - ); - const { provider_url } = deploymentUpdate.update.LeaseCreated; + const deploymentCreatedState = deploymentSteps.find((el) => + el.hasOwnProperty("DeploymentCreated") + )!; - const manifestUrl = new URL( - `/deployment/${dseq}/manifest`, - provider_url - ); - - await sendManifestToProvider( - manifestUrl.toString(), - manifest_sorted_json, + await confirmDeployment( + deploymentUpdate.update, + deploymentCreatedState, tlsCertificateData! ); - leaseCreated = true; + await setDeploymentAsActive(deploymentUpdate.id); + + await fetchDeployments(backendActor!); } } catch (e) { console.error(e); @@ -189,12 +200,7 @@ export default function NewDeployment() { setDeploymentSteps((prev) => [...prev, stepFailed]); - extractOk( - await backendActor!.update_deployment_state( - deploymentUpdate.id, - stepFailed - ) - ); + await updateDeploymentState(backendActor!, deploymentUpdate.id, stepFailed); } catch (e) { console.error("Failed to update deployment:", e); } @@ -204,32 +210,6 @@ export default function NewDeployment() { ); setIsDeploying(false); } - - try { - if (leaseCreated) { - closeWs(); - - const stepActive = { - Active: null, - }; - setDeploymentSteps((prev) => [...prev, stepActive]); - extractOk( - await backendActor!.update_deployment_state( - deploymentUpdate.id, - stepActive - ) - ); - - await fetchDeployments(backendActor!); - - setIsDeploying(false); - router.push("/dashboard"); - } - } catch (e) { - console.error("Failed to complete deployment:", e); - setDeploymentError("Failed to complete deployment, see console for details"); - setIsDeploying(false); - } }, [ tlsCertificateData, @@ -237,8 +217,8 @@ export default function NewDeployment() { deploymentSteps, backendActor, fetchDeployments, - router, closeWs, + setDeploymentAsActive, ] ); diff --git a/frontend/app/dashboard/page.tsx b/frontend/app/dashboard/page.tsx index 2ac85d0..d7c2e6d 100644 --- a/frontend/app/dashboard/page.tsx +++ b/frontend/app/dashboard/page.tsx @@ -4,19 +4,8 @@ import Tier from "@/components/Tier"; import { LoadingButton } from "@/components/loading-button"; import { Spinner } from "@/components/spinner"; import { Button } from "@/components/ui/button"; -import { - Card, - CardContent, - CardDescription, - CardFooter, - CardHeader, - CardTitle, -} from "@/components/ui/card"; -import { - Collapsible, - CollapsibleContent, - CollapsibleTrigger, -} from "@/components/ui/collapsible"; +import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, } from "@/components/ui/card"; +import { Collapsible, CollapsibleContent, CollapsibleTrigger, } from "@/components/ui/collapsible"; import { Dialog, DialogContent, @@ -144,7 +133,9 @@ export default function Dashboard() { {el.deployment.params.name} -
{el.id}
+ +
{el.id}
+
diff --git a/frontend/contexts/DeploymentContext.tsx b/frontend/contexts/DeploymentContext.tsx index d2d5d73..68f6c1d 100644 --- a/frontend/contexts/DeploymentContext.tsx +++ b/frontend/contexts/DeploymentContext.tsx @@ -5,6 +5,7 @@ import { createX509, X509CertificateData } from "@/lib/certificate"; import { type BackendActor } from "@/services/backend"; import { createContext, useCallback, useContext, useState } from "react"; import { getCurrentUser } from "@/services/user"; +import { completeDeployment, confirmDeployment, updateDeploymentState } from "@/services/deployment"; export type Deployments = OkType; @@ -25,19 +26,6 @@ export const DeploymentProvider: React.FC = ({ children const [tlsCertificateData, setCertificateData] = useState(null); const [deployments, setDeployments] = useState([]); - const fetchDeployments = useCallback(async (actor: BackendActor) => { - const res = await actor.get_deployments(); - - const _deployments = extractOk(res); - setDeployments( - _deployments.sort((el1, el2) => - getDeploymentCreatedDate(el2.deployment).getTime() - getDeploymentCreatedDate(el1.deployment).getTime() - ) - ); - - console.log("deployments", _deployments); - }, []); - const loadOrCreateCertificate = useCallback(async (actor: BackendActor): Promise => { let certData = (await getCurrentUser(actor)).mtls_certificate[0] || null; @@ -58,6 +46,70 @@ export const DeploymentProvider: React.FC = ({ children return certData!; }, []); + const completeDeployments = useCallback(async (actor: BackendActor, deployments: Deployments): Promise => { + let updatedCount = 0; + + for (const deployment of deployments) { + const lastState = deployment.deployment.state_history[deployment.deployment.state_history.length - 1][1]; + const deploymentCreatedState = deployment.deployment.state_history.find(([_, state]) => "DeploymentCreated" in state)![1]; + if ("LeaseCreated" in lastState) { + try { + const cert = await loadOrCreateCertificate(actor); + + await confirmDeployment( + lastState, + deploymentCreatedState, + cert! + ); + await completeDeployment( + actor, + deployment.id, + ); + + updatedCount++; + } catch (e) { + console.error("Failed to update deployment:", e); + + try { + const stepFailed = { + FailedOnClient: { + reason: JSON.stringify(e), + }, + }; + await updateDeploymentState(actor, deployment.id, stepFailed); + + updatedCount++; + } catch (e) { + console.error("Failed to update deployment:", e); + throw e; + } + } + + } + } + + return updatedCount; + }, [loadOrCreateCertificate]); + + const fetchDeployments = useCallback(async (actor: BackendActor) => { + const res = await actor.get_deployments(); + + const _deployments = extractOk(res); + + const updatedCount = await completeDeployments(actor, _deployments); + if (updatedCount > 0) { + return await fetchDeployments(actor); + } + + setDeployments( + _deployments.sort((el1, el2) => + getDeploymentCreatedDate(el2.deployment).getTime() - getDeploymentCreatedDate(el1.deployment).getTime() + ) + ); + + console.log("deployments", _deployments); + }, [completeDeployments]); + return ( { + try { + if ("LeaseCreated" in deploymentState) { + const { manifest_sorted_json, dseq } = extractDeploymentCreated(deploymentCreatedState); + + const { provider_url } = deploymentState.LeaseCreated; + + const manifestUrl = new URL( + `/deployment/${dseq}/manifest`, + provider_url + ); + + await sendManifestToProvider( + manifestUrl.toString(), + manifest_sorted_json, + cert! + ); + + } else { + throw new Error("Deployment state is not in LeaseCreated state"); + } + } catch (e) { + console.error("Failed to send manifest to provider", e); + throw e; + } +} + +export const updateDeploymentState = async (backendActor: BackendActor, deploymentId: string, step: DeploymentState) => { + try { + extractOk(await backendActor.update_deployment_state(deploymentId, step)); + } catch (e) { + console.error("Failed to update deployment state", e); + throw e; + } + +} + +export const completeDeployment = async (backendActor: BackendActor, deploymentId: string) => { + const stepActive = { + Active: null, + }; + + await updateDeploymentState(backendActor!, deploymentId, stepActive); +};