Skip to content

Commit

Permalink
Merge branch 'dev' into feat/upload-files-to-create-project
Browse files Browse the repository at this point in the history
  • Loading branch information
iannbing committed May 6, 2022
2 parents cc8deac + c8ffb91 commit f6c7de9
Show file tree
Hide file tree
Showing 7 changed files with 192 additions and 40 deletions.
16 changes: 16 additions & 0 deletions scripts/copy_data_from_orchest.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#!/bin/bash
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
DESTINATION="${DIR}/../backups/data"

pod_name=$(kubectl get pods -n orchest -l app.kubernetes.io/name=orchest-api \
--field-selector=status.phase=Running --no-headers \
--output=jsonpath={.items..metadata.name})

if [ -z "$pod_name" ]
then
echo "Orchest needs to be running to perform this operation."
exit 1
fi

echo "Copying the data directory to ${DESTINATION}"
kubectl cp "orchest/${pod_name}:/userdir/data" "$DESTINATION" > /dev/null
22 changes: 22 additions & 0 deletions scripts/copy_data_to_orchest.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#!/bin/bash
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
SOURCE="${DIR}/../backups/data"

pod_name=$(kubectl get pods -n orchest -l app.kubernetes.io/name=orchest-api \
--field-selector=status.phase=Running --no-headers \
--output=jsonpath={.items..metadata.name})

if [ -z "$pod_name" ]
then
echo "Orchest needs to be running to perform this operation."
exit 1
fi

if [ ! -d "${SOURCE}" ]
then
echo "Didn't find ${SOURCE}, exiting."
exit 1
fi

echo "Copying the data directory at ${SOURCE} into Orchest."
kubectl cp "${SOURCE}" "orchest/${pod_name}:/userdir" > /dev/null
16 changes: 16 additions & 0 deletions scripts/copy_projects_from_orchest.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#!/bin/bash
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
DESTINATION="${DIR}/../backups/projects"

pod_name=$(kubectl get pods -n orchest -l app.kubernetes.io/name=orchest-api \
--field-selector=status.phase=Running --no-headers \
--output=jsonpath={.items..metadata.name})

if [ -z "$pod_name" ]
then
echo "Orchest needs to be running to perform this operation."
exit 1
fi

echo "Copying projects to ${DESTINATION}"
kubectl cp "orchest/${pod_name}:/userdir/projects" "$DESTINATION" > /dev/null
22 changes: 22 additions & 0 deletions scripts/copy_projects_to_orchest.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#!/bin/bash
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
SOURCE="${DIR}/../backups/projects"

pod_name=$(kubectl get pods -n orchest -l app.kubernetes.io/name=orchest-api \
--field-selector=status.phase=Running --no-headers \
--output=jsonpath={.items..metadata.name})

if [ -z "$pod_name" ]
then
echo "Orchest needs to be running to perform this operation."
exit 1
fi

if [ ! -d "${SOURCE}" ]
then
echo "Didn't find ${SOURCE}, exiting."
exit 1
fi

echo "Copying the projects directory at ${SOURCE} into Orchest."
kubectl cp "${SOURCE}" "orchest/${pod_name}:/userdir" > /dev/null
9 changes: 8 additions & 1 deletion services/orchest-ctl/app/orchest/_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -729,7 +729,14 @@ def _update() -> None:
a user through the CLI. This code will change the state of the
deployment of Orchest. It assumes Orchest has been stopped.
"""
utils.echo("Updating...")
utils.echo(
"Orchest has gone through a breaking change to introduce a native kubernetes "
"operator for managing Orchest. You are on the latest pre-operator version "
"and cannot automatically update further. Head to the docs at "
"https://docs.orchest.io/en/stable/getting_started/installation.html for "
"instructions on how to move to this newer version of Orchest."
)
return

# Check if Orchest is actually stopped.
depls = k8sw.get_orchest_deployments()
Expand Down
145 changes: 107 additions & 38 deletions services/orchest-webserver/client/src/hooks/useCheckUpdate.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@ import { useLocalStorage } from "@/hooks/useLocalStorage";
import { siteMap } from "@/routingConfig";
import { OrchestVersion, UpdateInfo } from "@/types";
import Typography from "@mui/material/Typography";
import { fetcher } from "@orchest/lib-utils";
import { fetcher, hasValue } from "@orchest/lib-utils";
import React from "react";
import useSWRImmutable from "swr/immutable";
import { useCancelablePromise } from "./useCancelablePromise";

const isVersionLTE = (oldVersion: string, newVersion: string) => {
const [oldYear, oldMonth, oldPatch] = oldVersion.split(".");
Expand All @@ -28,46 +29,99 @@ const shouldPromptOrchestUpdate = (
skipVersion: string | null = null
) => {
// The latest version information has not yet been fetched by Orchest.
if (latestVersion === null) return false;
if (!hasValue(latestVersion)) return false;
if (isVersionLTE(latestVersion, currentVersion)) return false;
return skipVersion !== latestVersion;
};

const fetchOrchestVersion = () =>
fetcher<OrchestVersion>("/async/version").then(
(response) => response.version
);

const fetchLatestVersion = () =>
fetcher<UpdateInfo>("/async/orchest-update-info").then(
(response) => response.latest_version
);

// To limit the number of api calls and make sure only one prompt is shown,
// it is best to place this hook in top-level components (i.e. the ones
// defined in the routingConfig.tsx).
export const useCheckUpdate = () => {
const [skipVersion, setSkipVersion] = useLocalStorage("skip_version", null);
const { setConfirm, setAlert } = useAppContext();
const { navigateTo } = useCustomRoute();

const [skipVersion, setSkipVersion] = useLocalStorage<string | null>(
"skip_version",
null
);

// NOTE: this is a temporary alert that will be removed in the next release.
// ============================= start of the temporary code block ============================
const [
shouldShowBreakingChangeAlert,
setShouldShowBreakingChangeAlert,
] = useLocalStorage<boolean>(
"should_show_breaking_change_alert_05-2022",
true
);

const promptBreakingChangesAlert = React.useCallback(() => {
setAlert(
"Breaking changes in the next version",
<>
<Typography variant="body2">
{`There is a new version of Orchest that contains infrastructure breaking changes. Directly updating to the new version is unfortunately not possible.`}
</Typography>
<Typography variant="body2" sx={{ marginTop: 4 }}>
{`Please refer to `}
<a
target="_blank"
rel="noreferrer"
href="https://docs.orchest.io/en/stable/getting_started/installation.html "
>
the installation guide
</a>
{` to re-install Orchest with the new version.`}
</Typography>
</>,
(resolve) => {
setShouldShowBreakingChangeAlert(false);
resolve(true);
return true;
}
);
}, [setAlert, setShouldShowBreakingChangeAlert]);

// ============================= end of the temporary code block ============================

// Only make requests every hour, because the latest Orchest version gets
// fetched once per hour. Use `useSWRImmutable` to disable all kinds of
// automatic revalidation; just serve from cache and refresh cache
// once per hour.
const { data: orchestVersion } = useSWRImmutable<OrchestVersion>(
"/async/version",
fetcher,
const { data: orchestVersion } = useSWRImmutable(
true ? null : "/async/version",
fetchOrchestVersion,
{ refreshInterval: 3600000 }
);
const { data: updateInfo } = useSWRImmutable<UpdateInfo>(
"/async/orchest-update-info",
fetcher,
const { data: latestVersion } = useSWRImmutable(
true ? null : "/async/orchest-update-info",
fetchLatestVersion,
{ refreshInterval: 3600000 }
);

const { setConfirm } = useAppContext();
const { navigateTo } = useCustomRoute();

const promptUpdate = React.useCallback(
(currentVersion: string, latestVersion: string) => {
(localVersion: string, versionToUpdate: string) => {
setConfirm(
"Update available",
<>
<Typography variant="body2">
Orchest can be updated from <Code>{currentVersion}</Code> to{" "}
<Code>{latestVersion}</Code> . Would you like to update now?
{`Orchest can be updated from `}
<Code>{localVersion}</Code> to <Code>{versionToUpdate}</Code> .
Would you like to update now?
</Typography>
<Typography variant="body2" sx={{ marginTop: 4 }}>
Check out the{" "}
{`Check out the `}
<a href="https://github.com/orchest/orchest/releases/latest">
release notes
</a>
Expand All @@ -81,7 +135,7 @@ export const useCheckUpdate = () => {
return true;
},
onCancel: async (resolve) => {
setSkipVersion(updateInfo.latest_version);
setSkipVersion(versionToUpdate);
resolve(false);
return false;
},
Expand All @@ -90,57 +144,72 @@ export const useCheckUpdate = () => {
}
);
},
[setConfirm, setSkipVersion, updateInfo?.latest_version, navigateTo]
[setConfirm, setSkipVersion, navigateTo]
);

const handlePrompt = React.useCallback(
(
orchestVersion: OrchestVersion,
updateInfo: UpdateInfo,
localVersion: OrchestVersion["version"],
versionToUpdate: UpdateInfo["latest_version"],
skipVersion: string | null,
shouldPromptNoUpdate: boolean
) => {
const currentVersion = orchestVersion.version;
const latestVersion = updateInfo.latest_version;
if (!localVersion || !versionToUpdate) return;

const shouldPromptUpdate = shouldPromptOrchestUpdate(
currentVersion,
latestVersion,
localVersion,
versionToUpdate,
skipVersion
);
if (shouldPromptUpdate) {
promptUpdate(currentVersion, latestVersion);
promptUpdate(localVersion, versionToUpdate);
} else if (shouldPromptNoUpdate) {
setConfirm(
setAlert(
"No update available",
"There doesn't seem to be a new update available."
);
}
},
[setConfirm, promptUpdate]
[setAlert, promptUpdate]
);

const checkUpdateNow = async () => {
const { makeCancelable } = useCancelablePromise();

const checkUpdateNow = React.useCallback(async () => {
promptBreakingChangesAlert();
return;

// Use fetcher directly instead of mutate function from the SWR
// calls to prevent updating the values which would trigger the
// useEffect and thereby prompting the user twice. In addition,
// we want to be able to tell the user that no update is available
// if this function is invoked.
const [fetchedOrchestVersion, fetchedUpdateInfo] = await Promise.all([
fetcher<OrchestVersion>("/async/version"),
fetcher<UpdateInfo>("/async/orchest-update-info"),
]);
const [fetchedOrchestVersion, fetchedLatestVersion] = await makeCancelable(
Promise.all([fetchOrchestVersion(), fetchLatestVersion()])
);

if (fetchedOrchestVersion && fetchedUpdateInfo) {
handlePrompt(fetchedOrchestVersion, fetchedUpdateInfo, null, true);
if (fetchedOrchestVersion && fetchedLatestVersion) {
handlePrompt(fetchedOrchestVersion, fetchedLatestVersion, null, true);
}
};
}, [handlePrompt, makeCancelable]);

React.useEffect(() => {
if (orchestVersion && updateInfo) {
handlePrompt(orchestVersion, updateInfo, skipVersion, false);
if (shouldShowBreakingChangeAlert) {
promptBreakingChangesAlert();
}
return;

if (orchestVersion && latestVersion) {
handlePrompt(orchestVersion, latestVersion, skipVersion, false);
}
}, [orchestVersion, updateInfo, skipVersion, handlePrompt]);
}, [
orchestVersion,
latestVersion,
skipVersion,
handlePrompt,
promptBreakingChangesAlert,
shouldShowBreakingChangeAlert,
]);

return checkUpdateNow;
};
2 changes: 1 addition & 1 deletion services/orchest-webserver/client/src/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -410,5 +410,5 @@ export type UpdateInfo = {
};

export type OrchestVersion = {
version: string | null;
version: string | null | undefined;
};

0 comments on commit f6c7de9

Please sign in to comment.