Skip to content

Commit

Permalink
Add ability to kill function executions
Browse files Browse the repository at this point in the history
Commit description in progress...

Signed-off-by: Nick Gerace <[email protected]>
Co-authored-by: Victor Bustamante <[email protected]>
  • Loading branch information
vbustamante authored and nickgerace committed Aug 15, 2024
1 parent ac4ff1e commit fdde7ef
Show file tree
Hide file tree
Showing 35 changed files with 885 additions and 226 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

59 changes: 59 additions & 0 deletions app/web/src/components/Workspace/WorkspaceAdminDashboard.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<template>
<div
class="w-full h-full flex flex-col items-center relative overflow-hidden dark:bg-neutral-800 dark:text-shade-0 bg-neutral-50 text-neutral-900"
>
<Stack spacing="lg">
<span class="flex flex-row mt-10 font-bold text-3xl"
>Admin Dashboard</span
>
<Stack>
<h2 class="font-bold text-lg">KILL FUNCTION EXECUTION</h2>
<VormInput
v-model="funcRunId"
label="FuncRunId for function execution"
/>
<div class="flex flex-row-reverse gap-sm">
<VButton
:disabled="!funcRunId"
:requestStatus="cancelExecutionReqStatus"
class="flex-grow"
icon="plus-circle"
label="Kill function execution"
loadingText="Killing function execution"
tone="success"
@click="cancelExecution"
/>
</div>
</Stack>
</Stack>
</div>
</template>

<script lang="ts" setup>
import { onBeforeMount, ref } from "vue";
import { Stack, VormInput, VButton } from "@si/vue-lib/design-system";
import { useRouter } from "vue-router";
import { useAdminStore } from "@/store/admin.store";
import { useFeatureFlagsStore } from "@/store/feature_flags.store";
const adminStore = useAdminStore();
const featureFlagStore = useFeatureFlagsStore();
const router = useRouter();
onBeforeMount(async () => {
if (!featureFlagStore.ADMIN_PANEL_ACCESS) {
await router.push({ name: "workspace-single" });
}
});
const cancelExecutionReqStatus =
adminStore.getRequestStatus("CANCEL_EXECUTION");
const funcRunId = ref<string | null>(null);
const cancelExecution = () => {
if (funcRunId.value) {
adminStore.CANCEL_EXECUTION(funcRunId.value);
}
};
</script>
15 changes: 12 additions & 3 deletions app/web/src/components/layout/navbar/ProfileButton.vue
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
<template>
<NavbarButton
ref="navbarButtonRef"
tooltipText="Profile"
class="flex-none py-xs"
tooltipText="Profile"
>
<template #default="{ open, hovered }">
<div class="flex-row flex text-white items-center">
Expand All @@ -29,15 +29,21 @@
/>
<DropdownMenuItem
v-if="isDevMode"
linkToNamedRoute="workspace-dev-dashboard"
icon="cat"
label="Dev Dashboard"
linkToNamedRoute="workspace-dev-dashboard"
/>
<DropdownMenuItem
v-if="featureFlagsStore.ADMIN_PANEL_ACCESS"
icon="alert-triangle"
label="Admin Dashboard"
linkToNamedRoute="workspace-admin-dashboard"
/>
<DropdownMenuItem
class="profile-dropdown-menu-logout"
linkToNamedRoute="logout"
icon="logout"
label="Logout"
linkToNamedRoute="logout"
/>
</template>
<template #dropdownContentSecondary>
Expand Down Expand Up @@ -73,9 +79,12 @@ import SiArrow from "@/components/SiArrow.vue";
import { useAuthStore } from "@/store/auth.store";
import { isDevMode } from "@/utils/debug";
import { useWorkspacesStore } from "@/store/workspaces.store";
import { useFeatureFlagsStore } from "@/store/feature_flags.store";
import NavbarButton from "./NavbarButton.vue";
import UserIcon from "./UserIcon.vue";
const featureFlagsStore = useFeatureFlagsStore();
const AUTH_PORTAL_URL = import.meta.env.VITE_AUTH_PORTAL_URL;
const workspacesStore = useWorkspacesStore();
const authStore = useAuthStore();
Expand Down
6 changes: 6 additions & 0 deletions app/web/src/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,12 @@ const routes: RouteRecordRaw[] = [
},
],
},
{
path: "admin",
name: "workspace-admin-dashboard",
component: () =>
import("@/components/Workspace/WorkspaceAdminDashboard.vue"),
},
...(isDevMode
? [
{
Expand Down
30 changes: 30 additions & 0 deletions app/web/src/store/admin.store.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { addStoreHooks, ApiRequest } from "@si/vue-lib/pinia";
import { defineStore } from "pinia";
import { FuncRunId } from "@/store/func_runs.store";
import { useWorkspacesStore } from "./workspaces.store";

export const useAdminStore = () => {
const workspacesStore = useWorkspacesStore();
const workspaceId = workspacesStore.selectedWorkspacePk;

const API_PREFIX = `v2/workspaces/${workspaceId}/admin`;

return addStoreHooks(
workspaceId,
null,
defineStore(`ws${workspaceId || "NONE"}/admin`, {
state: () => ({}),
actions: {
async CANCEL_EXECUTION(funcRunId: FuncRunId) {
return new ApiRequest<null>({
method: "put",
url: `${API_PREFIX}/runs/${funcRunId}/cancel_execution`,
});
},
},
onActivated() {
return () => {};
},
}),
)();
};
1 change: 1 addition & 0 deletions app/web/src/store/feature_flags.store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ const FLAG_MAPPING = {
MODULES_TAB: "modules_tab",
FIRST_TIME_TUTORIAL_MODAL: "first_time_tutorial_modal",
DEV_SLICE_REBASING: "dev-slice-rebasing",
ADMIN_PANEL_ACCESS: "si_admin_panel_access",
};

type FeatureFlags = keyof typeof FLAG_MAPPING;
Expand Down
1 change: 1 addition & 0 deletions bin/lang-js/.eslintignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
node_modules
dist
coverage
examples
4 changes: 2 additions & 2 deletions bin/lang-js/.eslintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ module.exports = {
{
files: ["**/*.ts"],
rules: {
"no-console": "off",
"no-console": "off"
}
}
],
]
};
7 changes: 7 additions & 0 deletions lib/cyclone-core/src/cancel_execution.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
use serde::{Deserialize, Serialize};

#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CancelExecutionRequest {
pub execution_id: String,
}
2 changes: 2 additions & 0 deletions lib/cyclone-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

mod action_run;
mod before;
mod cancel_execution;
mod canonical_command;
mod component_view;
mod liveness;
Expand All @@ -30,6 +31,7 @@ pub use si_crypto::SensitiveStrings;

pub use action_run::{ActionRunRequest, ActionRunResultSuccess, ResourceStatus};
pub use before::BeforeFunction;
pub use cancel_execution::CancelExecutionRequest;
pub use canonical_command::{CanonicalCommand, CanonicalCommandError};
pub use component_view::{ComponentKind, ComponentView};
pub use liveness::{LivenessStatus, LivenessStatusParseError};
Expand Down
52 changes: 47 additions & 5 deletions lib/cyclone-core/src/progress.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
use serde::{de::DeserializeOwned, Deserialize, Serialize};

/// The error kind for function result failures when the error occurs within the veritech server.
const FUNCTION_RESULT_FAILURE_ERROR_KIND_VERITECH_SERVER: &str = "veritechServer";

/// A line of output, streamed from an executing function.
///
/// An instance of this type typically maps to a single line of output from a process--either on
Expand Down Expand Up @@ -97,11 +100,50 @@ pub enum FunctionResult<S> {

#[derive(Debug, Deserialize, Eq, PartialEq, Serialize, Clone)]
pub struct FunctionResultFailure {
pub execution_id: String,
pub error: FunctionResultFailureError,
// FIXME(nick,wendy): get the Utc::now() shape as well
// (perhaps struct Foo { raw: Utc::now(), timestamp: crate::timestamp() } )
pub timestamp: u64,
execution_id: String,
error: FunctionResultFailureError,
timestamp: u64,
}

impl FunctionResultFailure {
/// Creates a [`FunctionResultFailure`].
pub fn new(
execution_id: impl Into<String>,
error: FunctionResultFailureError,
timestamp: u64,
) -> Self {
Self {
execution_id: execution_id.into(),
error,
timestamp,
}
}

/// This kind of [`FunctionResultFailure`] occurs when the veritech server encounters an error.
pub fn new_for_veritech_server_error(
execution_id: impl Into<String>,
message: impl Into<String>,
timestamp: u64,
) -> Self {
Self {
execution_id: execution_id.into(),
error: FunctionResultFailureError {
kind: FUNCTION_RESULT_FAILURE_ERROR_KIND_VERITECH_SERVER.to_string(),
message: message.into(),
},
timestamp,
}
}

/// Returns a reference to the "execution_id".
pub fn execution_id(&self) -> &String {
&self.execution_id
}

/// Returns a reference to the "error".
pub fn error(&self) -> &FunctionResultFailureError {
&self.error
}
}

#[derive(Debug, Deserialize, Eq, PartialEq, Serialize, Clone)]
Expand Down
10 changes: 5 additions & 5 deletions lib/cyclone-server/src/execution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -471,14 +471,14 @@ where
fn from(value: LangServerResult<LangServerSuccess>) -> Self {
match value {
LangServerResult::Success(success) => Self::Success(success.into()),
LangServerResult::Failure(failure) => Self::Failure(FunctionResultFailure {
execution_id: failure.execution_id,
error: FunctionResultFailureError {
LangServerResult::Failure(failure) => Self::Failure(FunctionResultFailure::new(
failure.execution_id,
FunctionResultFailureError {
kind: failure.error.kind,
message: failure.error.message,
},
timestamp: crate::timestamp(),
}),
crate::timestamp(),
)),
}
}
}
Expand Down
9 changes: 6 additions & 3 deletions lib/dal/src/func/backend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use async_trait::async_trait;
use tokio::sync::mpsc;

use serde::{de::DeserializeOwned, Deserialize, Serialize};
use si_events::FuncRunId;
use strum::{AsRefStr, Display, EnumIter, EnumString};
use telemetry::prelude::*;
use thiserror::Error;
Expand Down Expand Up @@ -305,15 +306,17 @@ impl ToLabelList for FuncBackendKind {}
pub struct FuncDispatchContext {
pub veritech: VeritechClient,
pub output_tx: mpsc::Sender<OutputStream>,
pub func_run_id: FuncRunId,
}

impl FuncDispatchContext {
pub fn new(ctx: &DalContext) -> (Self, mpsc::Receiver<OutputStream>) {
pub fn new(ctx: &DalContext, func_run_id: FuncRunId) -> (Self, mpsc::Receiver<OutputStream>) {
let (output_tx, rx) = mpsc::channel(64);
(
Self {
veritech: ctx.veritech().clone(),
output_tx,
func_run_id,
},
rx,
)
Expand Down Expand Up @@ -391,9 +394,9 @@ pub trait FuncDispatch: std::fmt::Debug {
}
FunctionResult::Failure(failure) => {
return Err(span.record_err(FuncBackendError::ResultFailure {
kind: failure.error.kind,
kind: failure.error().kind.to_owned(),
backend,
message: failure.error.message,
message: failure.error().message.to_owned(),
}));
}
};
Expand Down
14 changes: 6 additions & 8 deletions lib/dal/src/func/backend/js_action.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,7 @@ impl FuncDispatch for FuncBackendJsAction {
before: Vec<BeforeFunction>,
) -> Box<Self> {
let request = ActionRunRequest {
// Once we start tracking the state of these executions, then this id will be useful,
// but for now it's passed along and back, and is opaue
execution_id: "ayrtonsennajscommand".to_string(),
execution_id: context.func_run_id.to_string(), // RIP PAULO - GONE (from si) BUT NOT FORGOTTEN
handler: handler.into(),
code_base64: code_base64.into(),
args: args.0,
Expand Down Expand Up @@ -75,26 +73,26 @@ impl FuncDispatch for FuncBackendJsAction {
FunctionResult::Failure(failure) => {
output_tx
.send(OutputStream {
execution_id: failure.execution_id.clone(),
execution_id: failure.execution_id().to_owned(),
stream: "return".to_owned(),
level: "error".to_owned(),
group: None,
message: failure.error.message.clone(),
message: failure.error().message.to_owned(),
timestamp: std::cmp::max(Utc::now().timestamp(), 0) as u64,
})
.await
.map_err(|_| FuncBackendError::SendError)?;

FunctionResult::Success(Self::Output {
execution_id: failure.execution_id,
execution_id: failure.execution_id().to_owned(),
payload: self
.request
.args
.pointer("/properties/resource/payload")
.cloned(),
status: ResourceStatus::Error,
message: Some(failure.error.message.clone()),
error: Some(serde_json::to_string(&failure.error)?),
message: Some(failure.error().message.clone()),
error: Some(serde_json::to_string(&failure.error())?),
})
}
};
Expand Down
Loading

0 comments on commit fdde7ef

Please sign in to comment.