forked from github-starred/komodo
add service user replacement in procedure
This commit is contained in:
@@ -33,7 +33,7 @@ impl Resolve<RunProcedure, User> for State {
|
||||
.await?;
|
||||
|
||||
let mut update =
|
||||
make_update(&procedure, Operation::StopContainer, &user);
|
||||
make_update(&procedure, Operation::RunProcedure, &user);
|
||||
update.in_progress();
|
||||
update.push_simple_log(
|
||||
"execute procedure",
|
||||
|
||||
@@ -8,7 +8,7 @@ use monitor_client::entities::{
|
||||
server::{Server, ServerStatus},
|
||||
tag::Tag,
|
||||
update::{ResourceTarget, Update, UpdateListItem},
|
||||
user::User,
|
||||
user::{admin_service_user, User},
|
||||
Operation,
|
||||
};
|
||||
use mungos::{
|
||||
@@ -60,6 +60,9 @@ pub fn make_update(
|
||||
}
|
||||
|
||||
pub async fn get_user(user_id: &str) -> anyhow::Result<User> {
|
||||
if let Some(user) = admin_service_user(user_id) {
|
||||
return Ok(user);
|
||||
}
|
||||
find_one_by_id(&db_client().await.users, user_id)
|
||||
.await
|
||||
.context("failed to query mongo for user")?
|
||||
|
||||
@@ -8,7 +8,7 @@ use monitor_client::{
|
||||
monitor_timestamp,
|
||||
procedure::{EnabledExecution, Procedure, ProcedureConfig},
|
||||
update::Update,
|
||||
user::User,
|
||||
user::procedure_user,
|
||||
},
|
||||
};
|
||||
use resolver_api::Resolve;
|
||||
@@ -123,7 +123,7 @@ async fn execute_execution(
|
||||
parent_id: &str,
|
||||
parent_name: &str,
|
||||
) -> anyhow::Result<()> {
|
||||
let user = User::admin_service_user("procedure");
|
||||
let user = procedure_user().to_owned();
|
||||
let update = match execution {
|
||||
Execution::None(_) => return Ok(()),
|
||||
Execution::RunProcedure(req) => {
|
||||
|
||||
@@ -4,7 +4,7 @@ use hex::ToHex;
|
||||
use hmac::{Hmac, Mac};
|
||||
use monitor_client::{
|
||||
api::execute,
|
||||
entities::{build::Build, repo::Repo, user::User},
|
||||
entities::{build::Build, repo::Repo, user::github_user},
|
||||
};
|
||||
use resolver_api::Resolve;
|
||||
use serde::Deserialize;
|
||||
@@ -80,7 +80,7 @@ async fn handle_build_webhook(
|
||||
State
|
||||
.resolve(
|
||||
execute::RunBuild { build: build_id },
|
||||
User::admin_service_user("github"),
|
||||
github_user().to_owned(),
|
||||
)
|
||||
.await?;
|
||||
Ok(())
|
||||
@@ -100,7 +100,7 @@ async fn handle_repo_clone_webhook(
|
||||
State
|
||||
.resolve(
|
||||
execute::CloneRepo { repo: repo_id },
|
||||
User::admin_service_user("github"),
|
||||
github_user().to_owned(),
|
||||
)
|
||||
.await?;
|
||||
Ok(())
|
||||
@@ -120,7 +120,7 @@ async fn handle_repo_pull_webhook(
|
||||
State
|
||||
.resolve(
|
||||
execute::PullRepo { repo: repo_id },
|
||||
User::admin_service_user("github"),
|
||||
github_user().to_owned(),
|
||||
)
|
||||
.await?;
|
||||
Ok(())
|
||||
|
||||
@@ -20,6 +20,14 @@ config.build_path = "."
|
||||
config.dockerfile_path = "frontend/Dockerfile"
|
||||
config.docker_account = "mbecker2020"
|
||||
|
||||
[[build]]
|
||||
name = "test-build"
|
||||
tags = ["test"]
|
||||
config.builder = "mogh-builder"
|
||||
config.repo = "mbecker20/docker_build_test"
|
||||
config.github_account = "mbecker20"
|
||||
config.docker_account = "mbecker2020"
|
||||
|
||||
[[builder]]
|
||||
name = "mogh-builder"
|
||||
description = "the builder for the mogh images"
|
||||
|
||||
@@ -20,4 +20,11 @@ tags = ["monitor", "v1", "frontend"]
|
||||
config.server = "mogh-server"
|
||||
config.redeploy_on_build = true
|
||||
config.image.type = "Build"
|
||||
config.image.params.build = "monitor-frontend-v1"
|
||||
config.image.params.build = "monitor-frontend-v1"
|
||||
|
||||
[[deployment]]
|
||||
name = "hello-world"
|
||||
tags = ["test"]
|
||||
config.server = "mogh-server"
|
||||
config.image.type = "Image"
|
||||
config.image.params.image = "hello-world"
|
||||
@@ -1,3 +1,5 @@
|
||||
use std::sync::OnceLock;
|
||||
|
||||
use mongo_indexed::derive::MongoIndexed;
|
||||
use mungos::mongodb::bson::{
|
||||
doc, serde_helpers::hex_string_as_object_id, Document,
|
||||
@@ -60,18 +62,40 @@ impl User {
|
||||
self.config = UserConfig::default();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn admin_service_user(id_name: impl Into<String>) -> User {
|
||||
let id_name: String = id_name.into();
|
||||
pub fn admin_service_user(user_id: &str) -> Option<User> {
|
||||
match user_id {
|
||||
"Procedure" => procedure_user().to_owned().into(),
|
||||
"Github" => github_user().to_owned().into(),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn procedure_user() -> &'static User {
|
||||
static PROCEDURE_USER: OnceLock<User> = OnceLock::new();
|
||||
PROCEDURE_USER.get_or_init(|| {
|
||||
let id_name = String::from("Procedure");
|
||||
User {
|
||||
id: id_name.clone(),
|
||||
username: id_name,
|
||||
admin: true,
|
||||
create_build_permissions: true,
|
||||
create_server_permissions: true,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn github_user() -> &'static User {
|
||||
static PROCEDURE_USER: OnceLock<User> = OnceLock::new();
|
||||
PROCEDURE_USER.get_or_init(|| {
|
||||
let id_name = String::from("Github");
|
||||
User {
|
||||
id: id_name.clone(),
|
||||
username: id_name,
|
||||
admin: true,
|
||||
..Default::default()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[typeshare]
|
||||
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
ConfirmUpdate,
|
||||
} from "@components/config/util";
|
||||
import { Section } from "@components/layouts";
|
||||
import { snake_case_to_upper_space_case } from "@lib/formatting";
|
||||
import { Types } from "@monitor/client";
|
||||
import { Button } from "@ui/button";
|
||||
import { Card, CardHeader, CardTitle, CardContent } from "@ui/card";
|
||||
@@ -122,9 +123,11 @@ export const Config = <T,>({
|
||||
<div className="flex flex-col gap-6 min-h-[500px] w-full">
|
||||
{Object.entries(components[show]).map(([k, v]) => (
|
||||
<Card className="w-full" key={k}>
|
||||
<CardHeader className="border-b">
|
||||
<CardTitle className="capitalize">{k} Settings</CardTitle>
|
||||
</CardHeader>
|
||||
{k && (
|
||||
<CardHeader className="border-b">
|
||||
<CardTitle>{snake_case_to_upper_space_case(k)}</CardTitle>
|
||||
</CardHeader>
|
||||
)}
|
||||
<CardContent className="flex flex-col gap-4 mt-4">
|
||||
<ConfigAgain
|
||||
config={config}
|
||||
|
||||
@@ -62,19 +62,27 @@ export const ConfigItem = ({
|
||||
export const ConfigInput = ({
|
||||
label,
|
||||
value,
|
||||
placeholder,
|
||||
disabled,
|
||||
onChange,
|
||||
onBlur,
|
||||
}: {
|
||||
label: string;
|
||||
value: string | number | undefined;
|
||||
onChange: (value: string) => void;
|
||||
onChange?: (value: string) => void;
|
||||
onBlur?: (value: string) => void;
|
||||
placeholder?: string;
|
||||
disabled?: boolean;
|
||||
}) => (
|
||||
<ConfigItem label={label}>
|
||||
<Input
|
||||
className="max-w-[75%] lg:max-w-[400px]"
|
||||
type={typeof value === "number" ? "number" : undefined}
|
||||
value={value}
|
||||
onChange={(e) => onChange(e.target.value)}
|
||||
// disabled={loading}
|
||||
onChange={(e) => onChange && onChange(e.target.value)}
|
||||
onBlur={(e) => onBlur && onBlur(e.target.value)}
|
||||
placeholder={placeholder}
|
||||
disabled={disabled}
|
||||
/>
|
||||
</ConfigItem>
|
||||
);
|
||||
@@ -109,6 +117,7 @@ export const DoubleInput = <
|
||||
onRightChange,
|
||||
onAdd,
|
||||
onRemove,
|
||||
inputClassName,
|
||||
}: {
|
||||
values: T[] | undefined;
|
||||
leftval: L;
|
||||
@@ -120,18 +129,21 @@ export const DoubleInput = <
|
||||
onRightChange: (value: T[R], i: number) => void;
|
||||
onAdd: () => void;
|
||||
onRemove: (i: number) => void;
|
||||
inputClassName?: string;
|
||||
}) => {
|
||||
return (
|
||||
<div className="flex flex-col gap-4">
|
||||
{values?.map((value, i) => (
|
||||
<div className="flex items-center justify-between gap-4" key={i}>
|
||||
<Input
|
||||
className={inputClassName}
|
||||
value={value[leftval] as any}
|
||||
placeholder={leftpl}
|
||||
onChange={(e) => onLeftChange(e.target.value as T[L], i)}
|
||||
/>
|
||||
:
|
||||
<Input
|
||||
className={inputClassName}
|
||||
value={value[rightval] as any}
|
||||
placeholder={rightpl}
|
||||
onChange={(e) => onRightChange(e.target.value as T[R], i)}
|
||||
@@ -200,7 +212,7 @@ export const ResourceSelector = ({
|
||||
{`No ${type}s Found`}
|
||||
<SearchX className="w-3 h-3" />
|
||||
</CommandEmpty>
|
||||
|
||||
|
||||
<CommandGroup>
|
||||
{resources.map((resource) => (
|
||||
<CommandItem
|
||||
@@ -235,9 +247,11 @@ export const AccountSelector = ({
|
||||
selected: string | undefined;
|
||||
onSelect: (id: string) => void;
|
||||
}) => {
|
||||
const request =
|
||||
type === "Server" ? "GetAvailableAccounts" : "GetBuilderAvailableAccounts";
|
||||
const accounts = useRead(request, { server: id! }, { enabled: !!id }).data;
|
||||
const [request, params] =
|
||||
type === "Server"
|
||||
? ["GetAvailableAccounts", { server: id! }]
|
||||
: ["GetBuilderAvailableAccounts", { builder: id }];
|
||||
const accounts = useRead(request as any, params, { enabled: !!id }).data;
|
||||
return (
|
||||
<ConfigItem label={`${account_type} Account`}>
|
||||
<Select
|
||||
@@ -254,7 +268,7 @@ export const AccountSelector = ({
|
||||
{type === "Server" && (
|
||||
<SelectItem value={" "}>Same as build</SelectItem>
|
||||
)}
|
||||
{accounts?.[account_type]?.map((account) => (
|
||||
{(accounts as any)?.[account_type]?.map((account: string) => (
|
||||
<SelectItem key={account} value={account}>
|
||||
{account}
|
||||
</SelectItem>
|
||||
|
||||
@@ -22,7 +22,7 @@ export const EnvVars = ({
|
||||
className="flex-col gap-4 items-start"
|
||||
>
|
||||
<Textarea
|
||||
className="min-h-[300px]"
|
||||
className="min-h-[400px]"
|
||||
placeholder="VARIABLE=value"
|
||||
value={env}
|
||||
onChange={(e) => setEnv(e.target.value)}
|
||||
|
||||
@@ -31,7 +31,7 @@ export const NetworkModeSelector = ({
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{networks?.map((network) => (
|
||||
<SelectItem key={network.Id} value={network.Name ?? ""}>
|
||||
<SelectItem key={network.Id} value={network.Name ?? ""} className="cursor-pointer">
|
||||
{network.Name}
|
||||
</SelectItem>
|
||||
))}
|
||||
|
||||
@@ -31,9 +31,11 @@ export const RestartModeSelector = ({
|
||||
<SelectItem
|
||||
key={mode}
|
||||
value={Types.RestartMode[mode]}
|
||||
className="capitalize"
|
||||
className="capitalize cursor-pointer"
|
||||
>
|
||||
{format_mode(Types.RestartMode[mode])}
|
||||
{mode === "NoRestart"
|
||||
? "Don't Restart"
|
||||
: format_mode(Types.RestartMode[mode])}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
import { ConfigInput, ConfigItem } from "@components/config/util";
|
||||
import { Types } from "@monitor/client";
|
||||
import { Input } from "@ui/input";
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectGroup,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from "@ui/select";
|
||||
import { useToast } from "@ui/use-toast";
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
export const TerminationSignals = ({
|
||||
args,
|
||||
set,
|
||||
}: {
|
||||
args: Types.TerminationSignalLabel[];
|
||||
set: (input: Partial<Types.DeploymentConfig>) => void;
|
||||
}) => {
|
||||
return (
|
||||
<ConfigItem label="Termination Signals">
|
||||
<div>
|
||||
{args.map((arg, i) => {
|
||||
return <></>;
|
||||
})}
|
||||
</div>
|
||||
</ConfigItem>
|
||||
);
|
||||
};
|
||||
|
||||
export const DefaultTerminationSignal = ({
|
||||
arg,
|
||||
set,
|
||||
}: {
|
||||
arg?: Types.TerminationSignal;
|
||||
set: (input: Partial<Types.DeploymentConfig>) => void;
|
||||
}) => {
|
||||
return (
|
||||
<ConfigItem label="Default Termination Signal">
|
||||
<Select
|
||||
value={arg}
|
||||
onValueChange={(value) =>
|
||||
set({ termination_signal: value as Types.TerminationSignal })
|
||||
}
|
||||
>
|
||||
<SelectTrigger className="w-[150px]">
|
||||
<SelectValue placeholder="Select Type" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectGroup>
|
||||
{Object.values(Types.TerminationSignal)
|
||||
.reverse()
|
||||
.map((term_signal) => (
|
||||
<SelectItem key={term_signal} value={term_signal} className="cursor-pointer">
|
||||
{term_signal}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectGroup>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</ConfigItem>
|
||||
);
|
||||
};
|
||||
|
||||
export const TerminationTimeout = ({
|
||||
arg,
|
||||
set,
|
||||
}: {
|
||||
arg: number;
|
||||
set: (input: Partial<Types.DeploymentConfig>) => void;
|
||||
}) => {
|
||||
const { toast } = useToast();
|
||||
const [input, setInput] = useState(arg.toString());
|
||||
useEffect(() => {
|
||||
setInput(arg.toString());
|
||||
}, [arg]);
|
||||
return (
|
||||
<ConfigItem label="Termination Timeout">
|
||||
<div className="flex items-center justify-between gap-4">
|
||||
<Input
|
||||
className="w-[100px]"
|
||||
placeholder="time in seconds"
|
||||
value={input}
|
||||
onChange={(e) => setInput(e.target.value)}
|
||||
onBlur={(e) => {
|
||||
const num = Number(e.target.value);
|
||||
if (num || num === 0) {
|
||||
set({ termination_timeout: num });
|
||||
} else {
|
||||
toast({ title: "Termination timeout must be a number" });
|
||||
setInput(arg.toString());
|
||||
}
|
||||
}}
|
||||
/>
|
||||
seconds
|
||||
</div>
|
||||
</ConfigItem>
|
||||
);
|
||||
};
|
||||
@@ -10,6 +10,7 @@ export const VolumesConfig = ({
|
||||
}) => (
|
||||
<ConfigItem label="Volumes" className="items-start">
|
||||
<DoubleInput
|
||||
inputClassName="w-[300px] max-w-full"
|
||||
values={volumes}
|
||||
leftval="local"
|
||||
leftpl="Local"
|
||||
|
||||
@@ -15,6 +15,10 @@ import { EnvVars } from "./components/environment";
|
||||
import { VolumesConfig } from "./components/volumes";
|
||||
import { ExtraArgs } from "./components/extra-args";
|
||||
import { Config } from "@components/config";
|
||||
import {
|
||||
DefaultTerminationSignal,
|
||||
TerminationTimeout,
|
||||
} from "./components/term-signal";
|
||||
|
||||
export const ServerSelector = ({
|
||||
selected,
|
||||
@@ -53,7 +57,7 @@ export const DeploymentConfig = ({ id }: { id: string }) => {
|
||||
onSave={() => mutate({ id, config: update })}
|
||||
components={{
|
||||
general: {
|
||||
general: {
|
||||
"": {
|
||||
server_id: (value, set) => (
|
||||
<ServerSelector selected={value} set={set} />
|
||||
),
|
||||
@@ -72,9 +76,6 @@ export const DeploymentConfig = ({ id }: { id: string }) => {
|
||||
restart: (value, set) => (
|
||||
<RestartModeSelector selected={value} set={set} />
|
||||
),
|
||||
extra_args: (value, set) => (
|
||||
<ExtraArgs args={value ?? []} set={set} />
|
||||
),
|
||||
process_args: (value, set) => (
|
||||
<ConfigInput
|
||||
label="Process Args"
|
||||
@@ -97,6 +98,23 @@ export const DeploymentConfig = ({ id }: { id: string }) => {
|
||||
volumes: {
|
||||
volumes: (v, set) => <VolumesConfig volumes={v ?? []} set={set} />,
|
||||
},
|
||||
extra_args: {
|
||||
extra_args: (value, set) => (
|
||||
<ExtraArgs args={value ?? []} set={set} />
|
||||
),
|
||||
},
|
||||
termination: {
|
||||
termination_signal: (value, set) => (
|
||||
<DefaultTerminationSignal arg={value} set={set} />
|
||||
),
|
||||
termination_timeout: (value, set) => (
|
||||
<TerminationTimeout arg={value} set={set} />
|
||||
),
|
||||
},
|
||||
settings: {
|
||||
send_alerts: true,
|
||||
redeploy_on_build: true,
|
||||
},
|
||||
},
|
||||
environment: {
|
||||
environment: {
|
||||
|
||||
@@ -93,7 +93,7 @@ export const DeploymentLogs = ({ id }: { id: string }) => {
|
||||
>
|
||||
{["stdout", "stderr"].map((t) => (
|
||||
<TabsContent key={t} className="h-full relative" value={t}>
|
||||
<div className="h-[60vh] overflow-y-auto">
|
||||
<div className="h-[70vh] overflow-y-auto">
|
||||
<pre id={t} className="-scroll-mt-24">
|
||||
{logs?.[t as keyof typeof logs] || `no ${t} logs`}
|
||||
</pre>
|
||||
|
||||
@@ -49,22 +49,22 @@ export const WithLoading = ({
|
||||
return <>{children}</>;
|
||||
};
|
||||
|
||||
export const ConfigInput = ({
|
||||
placeholder,
|
||||
value,
|
||||
onChange,
|
||||
}: {
|
||||
placeholder: string;
|
||||
value: string | undefined;
|
||||
onChange: (s: string) => void;
|
||||
}) => (
|
||||
<Input
|
||||
placeholder={placeholder}
|
||||
className="max-w-[500px]"
|
||||
value={value}
|
||||
onChange={({ target }) => onChange(target.value)}
|
||||
/>
|
||||
);
|
||||
// export const ConfigInput = ({
|
||||
// placeholder,
|
||||
// value,
|
||||
// onChange,
|
||||
// }: {
|
||||
// placeholder: string;
|
||||
// value: string | undefined;
|
||||
// onChange: (s: string) => void;
|
||||
// }) => (
|
||||
// <Input
|
||||
// placeholder={placeholder}
|
||||
// className="max-w-[500px]"
|
||||
// value={value}
|
||||
// onChange={({ target }) => onChange(target.value)}
|
||||
// />
|
||||
// );
|
||||
|
||||
export const ActionButton = forwardRef<
|
||||
HTMLButtonElement,
|
||||
|
||||
@@ -32,6 +32,7 @@ export const fmt_duration = (start_ts: number, end_ts: number) => {
|
||||
|
||||
/// list_all_items => List All Items
|
||||
export function snake_case_to_upper_space_case(snake: string) {
|
||||
if (snake.length === 0) return "";
|
||||
return snake
|
||||
.split("_")
|
||||
.map((item) => item[0].toUpperCase() + item.slice(1))
|
||||
|
||||
Reference in New Issue
Block a user