forked from github-starred/komodo
configure aws config on builds
This commit is contained in:
@@ -89,19 +89,31 @@ fn default_core_mongo_db_name() -> String {
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
|
||||
pub struct AwsBuilderConfig {
|
||||
#[serde(skip_serializing)]
|
||||
pub access_key_id: String,
|
||||
|
||||
#[serde(skip_serializing)]
|
||||
pub secret_access_key: String,
|
||||
|
||||
pub default_ami_id: String,
|
||||
pub default_subnet_id: String,
|
||||
pub default_key_pair_name: String,
|
||||
|
||||
#[serde(default)]
|
||||
pub available_ami_accounts: AvailableAmiAccounts,
|
||||
|
||||
#[serde(default = "default_aws_region")]
|
||||
pub default_region: String,
|
||||
|
||||
#[serde(default = "default_volume_gb")]
|
||||
pub default_volume_gb: i32,
|
||||
|
||||
#[serde(default = "default_instance_type")]
|
||||
pub default_instance_type: String,
|
||||
|
||||
#[serde(default)]
|
||||
pub default_security_group_ids: Vec<String>,
|
||||
|
||||
#[serde(default)]
|
||||
pub default_assign_public_ip: bool,
|
||||
}
|
||||
@@ -118,6 +130,17 @@ fn default_instance_type() -> String {
|
||||
String::from("m5.2xlarge")
|
||||
}
|
||||
|
||||
pub type AvailableAmiAccounts = HashMap<String, AmiAccounts>; // (ami_id, AmiAccounts)
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
|
||||
pub struct AmiAccounts {
|
||||
pub name: String,
|
||||
#[serde(default)]
|
||||
pub github: Vec<String>,
|
||||
#[serde(default)]
|
||||
pub docker: Vec<String>,
|
||||
}
|
||||
|
||||
pub type GithubUsername = String;
|
||||
pub type GithubToken = String;
|
||||
pub type GithubAccounts = HashMap<GithubUsername, GithubToken>;
|
||||
|
||||
@@ -43,6 +43,9 @@ default_subnet_id = "your_default_subnet_id"
|
||||
default_security_group_ids = ["sg_id_1", "sg_id_2"]
|
||||
default_assign_public_ip = false
|
||||
|
||||
[aws.available_ami_accounts]
|
||||
your_periphery_ami = { name = "default ami", github = ["github_username"], docker = ["docker_username"] }
|
||||
|
||||
[github_oauth]
|
||||
enabled = true
|
||||
id = "your_github_client_id"
|
||||
|
||||
@@ -298,8 +298,8 @@ impl State {
|
||||
self.update_update(update).await?;
|
||||
return Err(e);
|
||||
}
|
||||
let (server, aws_client, log) = res.unwrap();
|
||||
update.logs.push(log);
|
||||
let (server, aws_client, logs) = res.unwrap();
|
||||
update.logs.extend(logs);
|
||||
self.update_update(update.clone()).await?;
|
||||
(server, aws_client)
|
||||
} else {
|
||||
@@ -405,11 +405,11 @@ impl State {
|
||||
async fn create_ec2_instance_for_build(
|
||||
&self,
|
||||
build: &Build,
|
||||
) -> anyhow::Result<(Ec2Instance, Option<aws::Client>, Log)> {
|
||||
) -> anyhow::Result<(Ec2Instance, Option<aws::Client>, Vec<Log>)> {
|
||||
if build.aws_config.is_none() {
|
||||
return Err(anyhow!("build has no aws_config attached"));
|
||||
}
|
||||
let start_ts = monitor_timestamp();
|
||||
let start_instance_ts = monitor_timestamp();
|
||||
let aws_config = build.aws_config.as_ref().unwrap();
|
||||
let region = aws_config
|
||||
.region
|
||||
@@ -464,20 +464,29 @@ impl State {
|
||||
assign_public_ip,
|
||||
)
|
||||
.await?;
|
||||
let instance_id = &instance.instance_id;
|
||||
let start_log = Log {
|
||||
stage: "start build instance".to_string(),
|
||||
success: true,
|
||||
stdout: format!("instance id: {instance_id}\nami id: {ami_id}\ninstance type: {instance_type}\nvolume size: {volume_size_gb} GB\nsubnet id: {subnet_id}\nsecurity groups: {readable_sec_group_ids}"),
|
||||
start_ts: start_instance_ts,
|
||||
end_ts: monitor_timestamp(),
|
||||
..Default::default()
|
||||
};
|
||||
let start_connect_ts = monitor_timestamp();
|
||||
let mut res = Ok(String::new());
|
||||
for _ in 0..BUILDER_POLL_MAX_TRIES {
|
||||
let status = self.periphery.health_check(&instance.server).await;
|
||||
if let Ok(_) = status {
|
||||
let instance_id = &instance.instance_id;
|
||||
let log = Log {
|
||||
stage: "start build instance".to_string(),
|
||||
let connect_log = Log {
|
||||
stage: "build instance connected".to_string(),
|
||||
success: true,
|
||||
stdout: format!("instance id: {instance_id}\nami id: {ami_id}\ninstance type: {instance_type}\nvolume size: {volume_size_gb} GB\nsubnet id: {subnet_id}\nsecurity groups: {readable_sec_group_ids}"),
|
||||
start_ts,
|
||||
stdout: "established contact with periphery on builder".to_string(),
|
||||
start_ts: start_connect_ts,
|
||||
end_ts: monitor_timestamp(),
|
||||
..Default::default()
|
||||
};
|
||||
return Ok((instance, Some(aws_client), log));
|
||||
return Ok((instance, Some(aws_client), vec![start_log, connect_log]));
|
||||
}
|
||||
res = status;
|
||||
tokio::time::sleep(Duration::from_secs(BUILDER_POLL_RATE_SECS)).await;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useNavigate } from "@solidjs/router";
|
||||
import { Component, createSignal } from "solid-js";
|
||||
import { Component, createSignal, Show } from "solid-js";
|
||||
import { client, pushNotification } from "..";
|
||||
import { useAppState } from "../state/StateProvider";
|
||||
import { Build, Deployment } from "../types";
|
||||
@@ -42,12 +42,11 @@ const CopyMenu: Component<{
|
||||
if (p.type === "build") {
|
||||
promise = client.copy_build(p.id, {
|
||||
name: newName(),
|
||||
server_id: selectedId(),
|
||||
});
|
||||
} else {
|
||||
promise = client.copy_deployment(p.id, {
|
||||
name: newName(),
|
||||
server_id: selectedId(),
|
||||
server_id: selectedId()!,
|
||||
});
|
||||
}
|
||||
toggleShow();
|
||||
@@ -75,18 +74,20 @@ const CopyMenu: Component<{
|
||||
value={newName()}
|
||||
onEdit={setNewName}
|
||||
/>
|
||||
<Selector
|
||||
label="target: "
|
||||
selected={selectedId()}
|
||||
items={servers.ids()!}
|
||||
onSelect={setSelected}
|
||||
itemMap={(id) => servers.get(id)!.server.name}
|
||||
targetClass="blue"
|
||||
targetStyle={{ display: "flex", gap: "0.5rem" }}
|
||||
searchStyle={{ width: "100%" }}
|
||||
position="bottom right"
|
||||
useSearch
|
||||
/>
|
||||
<Show when={p.type === "deployment"}>
|
||||
<Selector
|
||||
label="target: "
|
||||
selected={selectedId()!}
|
||||
items={servers.ids()!}
|
||||
onSelect={setSelected}
|
||||
itemMap={(id) => servers.get(id)!.server.name}
|
||||
targetClass="blue"
|
||||
targetStyle={{ display: "flex", gap: "0.5rem" }}
|
||||
searchStyle={{ width: "100%" }}
|
||||
position="bottom right"
|
||||
useSearch
|
||||
/>
|
||||
</Show>
|
||||
</Flex>
|
||||
<ConfirmButton
|
||||
class="green"
|
||||
|
||||
@@ -24,7 +24,7 @@ const Actions: Component<{}> = (p) => {
|
||||
build().permissions![getId(user())] === PermissionLevel.Execute ||
|
||||
build().permissions![getId(user())] === PermissionLevel.Update;
|
||||
return (
|
||||
<Show when={userCanExecute() && server()?.status === ServerStatus.Ok}>
|
||||
<Show when={userCanExecute() && (server() ? server()?.status === ServerStatus.Ok : true)}>
|
||||
<Grid class={combineClasses("card shadow")} gridTemplateRows="auto 1fr">
|
||||
<h1>actions</h1>
|
||||
<Grid style={{ height: "fit-content" }}>
|
||||
|
||||
@@ -2,9 +2,7 @@ import { useNavigate, useParams } from "@solidjs/router";
|
||||
import { Component, createEffect, onCleanup, Show } from "solid-js";
|
||||
import { useAppDimensions } from "../../state/DimensionProvider";
|
||||
import { useAppState } from "../../state/StateProvider";
|
||||
import { useUser } from "../../state/UserProvider";
|
||||
import { Operation, PermissionLevel } from "../../types";
|
||||
import { combineClasses, getId } from "../../util/helpers";
|
||||
import { Operation } from "../../types";
|
||||
import NotFound from "../NotFound";
|
||||
import Grid from "../shared/layout/Grid";
|
||||
import Actions from "./Actions";
|
||||
|
||||
@@ -70,13 +70,15 @@ const Header: Component<{}> = (p) => {
|
||||
</Flex>
|
||||
<Flex alignItems="center" justifyContent="space-between">
|
||||
<Flex alignItems="center">
|
||||
<A
|
||||
href={`/server/${build().server_id}`}
|
||||
class="text-hover"
|
||||
style={{ opacity: 0.7, padding: 0 }}
|
||||
>
|
||||
{server()?.server.name}
|
||||
</A>
|
||||
<Show when={server()} fallback={<div style={{ opacity: 0.7 }}>{build().aws_config ? "aws build" : ""}</div>}>
|
||||
<A
|
||||
href={`/server/${build().server_id}`}
|
||||
class="text-hover"
|
||||
style={{ opacity: 0.7, padding: 0 }}
|
||||
>
|
||||
{server()?.server.name}
|
||||
</A>
|
||||
</Show>
|
||||
<div style={{ opacity: 0.7 }}>build</div>
|
||||
</Flex>
|
||||
<div style={{ opacity: 0.7 }}>
|
||||
|
||||
@@ -2,12 +2,10 @@ import { useParams } from "@solidjs/router";
|
||||
import { Component, Show } from "solid-js";
|
||||
import { useAppState } from "../../../state/StateProvider";
|
||||
import { useUser } from "../../../state/UserProvider";
|
||||
import { PermissionLevel } from "../../../types";
|
||||
import { getId } from "../../../util/helpers";
|
||||
import SimpleTabs from "../../shared/tabs/SimpleTabs";
|
||||
import { Tab } from "../../shared/tabs/Tabs";
|
||||
import BuildConfig from "./build-config/BuildConfig";
|
||||
import GitConfig from "./git-config/GitConfig";
|
||||
import BuilderConfig from "./builder/BuilderConfig";
|
||||
import BuildConfig from "./config/BuildConfig";
|
||||
import Owners from "./Permissions";
|
||||
import { ConfigProvider } from "./Provider";
|
||||
|
||||
@@ -24,12 +22,12 @@ const BuildTabs: Component<{}> = (p) => {
|
||||
tabs={
|
||||
[
|
||||
{
|
||||
title: "repo",
|
||||
element: () => <GitConfig />,
|
||||
title: "config",
|
||||
element: () => <BuildConfig />,
|
||||
},
|
||||
{
|
||||
title: "build",
|
||||
element: () => <BuildConfig />,
|
||||
title: "builder",
|
||||
element: () => <BuilderConfig />
|
||||
},
|
||||
user().admin && {
|
||||
title: "collaborators",
|
||||
|
||||
116
frontend/src/components/build/tabs/builder/AwsBuilderConfig.tsx
Normal file
116
frontend/src/components/build/tabs/builder/AwsBuilderConfig.tsx
Normal file
@@ -0,0 +1,116 @@
|
||||
import { Component, Show } from "solid-js";
|
||||
import { useAppState } from "../../../../state/StateProvider";
|
||||
import Input from "../../../shared/Input";
|
||||
import Flex from "../../../shared/layout/Flex";
|
||||
import Selector from "../../../shared/menu/Selector";
|
||||
import { useConfig } from "../Provider";
|
||||
|
||||
const AwsBuilderConfig: Component<{}> = (p) => {
|
||||
const { build } = useConfig();
|
||||
return (
|
||||
<>
|
||||
<Ami />
|
||||
<InstanceType />
|
||||
<VolumeSize />
|
||||
<Show when={!build.updated}>
|
||||
<div style={{ height: "4rem" }} />
|
||||
</Show>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const Ami: Component = () => {
|
||||
const { aws_builder_config } = useAppState();
|
||||
const { build, setBuild, userCanUpdate } = useConfig();
|
||||
const get_ami_id = () => {
|
||||
if (build.aws_config?.ami_id) {
|
||||
return build.aws_config.ami_id;
|
||||
} else {
|
||||
return aws_builder_config()?.default_ami_id || "unknown";
|
||||
}
|
||||
};
|
||||
const get_ami_name = (ami_id: string) => {
|
||||
if (aws_builder_config() === undefined || ami_id === "unknown")
|
||||
return "unknown";
|
||||
return (
|
||||
aws_builder_config()!.available_ami_accounts![ami_id]?.name || "unknown"
|
||||
);
|
||||
};
|
||||
const ami_ids = () => {
|
||||
if (aws_builder_config() === undefined) return [];
|
||||
return Object.keys(aws_builder_config()!.available_ami_accounts!);
|
||||
};
|
||||
return (
|
||||
<Flex
|
||||
class="config-item shadow"
|
||||
alignItems="center"
|
||||
justifyContent="space-between"
|
||||
>
|
||||
<h1>ami</h1>
|
||||
<Selector
|
||||
targetClass="blue"
|
||||
selected={get_ami_id()}
|
||||
items={ami_ids()}
|
||||
onSelect={(ami_id) => setBuild("aws_config", "ami_id", ami_id)}
|
||||
itemMap={get_ami_name}
|
||||
position="bottom right"
|
||||
disabled={!userCanUpdate()}
|
||||
useSearch
|
||||
/>
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
const InstanceType: Component = () => {
|
||||
const { aws_builder_config } = useAppState();
|
||||
const { build, setBuild, userCanUpdate } = useConfig();
|
||||
return (
|
||||
<Flex
|
||||
class="config-item shadow"
|
||||
alignItems="center"
|
||||
justifyContent="space-between"
|
||||
>
|
||||
<h1>instance type</h1>
|
||||
<Input
|
||||
placeholder={aws_builder_config()?.default_instance_type}
|
||||
value={build.aws_config?.instance_type}
|
||||
onEdit={(instance_type) =>
|
||||
setBuild("aws_config", "instance_type", instance_type)
|
||||
}
|
||||
disabled={!userCanUpdate()}
|
||||
/>
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
const VolumeSize: Component = () => {
|
||||
const { aws_builder_config } = useAppState();
|
||||
const { build, setBuild, userCanUpdate } = useConfig();
|
||||
return (
|
||||
<Flex
|
||||
class="config-item shadow"
|
||||
alignItems="center"
|
||||
justifyContent="space-between"
|
||||
>
|
||||
<h1>volume size</h1>
|
||||
<Flex gap="0.25rem" alignItems="center">
|
||||
<Input
|
||||
style={{ width: "4rem" }}
|
||||
placeholder={aws_builder_config()?.default_volume_gb?.toString()}
|
||||
value={
|
||||
build.aws_config?.volume_gb
|
||||
? build.aws_config.volume_gb.toString()
|
||||
: ""
|
||||
}
|
||||
onEdit={(volume_size) =>
|
||||
setBuild("aws_config", "volume_gb", Number(volume_size))
|
||||
}
|
||||
disabled={!userCanUpdate()}
|
||||
/>
|
||||
GB
|
||||
</Flex>
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
export default AwsBuilderConfig;
|
||||
@@ -1,43 +1,27 @@
|
||||
import { Component, Show } from "solid-js";
|
||||
import { pushNotification, URL } from "../../../..";
|
||||
import { combineClasses, copyToClipboard, getId } from "../../../../util/helpers";
|
||||
import ConfirmButton from "../../../shared/ConfirmButton";
|
||||
import Icon from "../../../shared/Icon";
|
||||
import Flex from "../../../shared/layout/Flex";
|
||||
import Grid from "../../../shared/layout/Grid";
|
||||
import Loading from "../../../shared/loading/Loading";
|
||||
import { useConfig } from "../Provider";
|
||||
import Git from "./Git";
|
||||
// import OnClone from "./OnClone";
|
||||
import Loading from "../../../shared/loading/Loading";
|
||||
import BuilderType from "./BuilderType";
|
||||
import BuilderServer from "./BuilderServer";
|
||||
import AwsBuilderConfig from "./AwsBuilderConfig";
|
||||
|
||||
const GitConfig: Component<{}> = (p) => {
|
||||
const BuilderConfig: Component<{}> = (p) => {
|
||||
const { build, reset, save, userCanUpdate } = useConfig();
|
||||
const listenerUrl = () => `${URL}/api/listener/build/${getId(build)}`;
|
||||
return (
|
||||
<Show when={build.loaded}>
|
||||
<Grid class="config">
|
||||
<Grid class="config-items scroller">
|
||||
<Git />
|
||||
{/* <OnClone /> */}
|
||||
<Show when={userCanUpdate()}>
|
||||
<Grid class={combineClasses("config-item shadow")}>
|
||||
<h1>webhook url</h1>
|
||||
<Flex justifyContent="space-between" alignItems="center">
|
||||
<div class="ellipsis" style={{ width: "250px" }}>
|
||||
{listenerUrl()}
|
||||
</div>
|
||||
<ConfirmButton
|
||||
class="blue"
|
||||
onFirstClick={() => {
|
||||
copyToClipboard(listenerUrl());
|
||||
pushNotification("good", "copied url to clipboard");
|
||||
}}
|
||||
confirm={<Icon type="check" />}
|
||||
>
|
||||
<Icon type="clipboard" />
|
||||
</ConfirmButton>
|
||||
</Flex>
|
||||
</Grid>
|
||||
<BuilderType />
|
||||
<Show when={build.server_id}>
|
||||
<BuilderServer />
|
||||
<div style={{ height: "12rem" }} />
|
||||
</Show>
|
||||
<Show when={build.aws_config}>
|
||||
<AwsBuilderConfig />
|
||||
</Show>
|
||||
</Grid>
|
||||
<Show when={userCanUpdate() && build.updated}>
|
||||
@@ -66,4 +50,4 @@ const GitConfig: Component<{}> = (p) => {
|
||||
);
|
||||
};
|
||||
|
||||
export default GitConfig;
|
||||
export default BuilderConfig;
|
||||
46
frontend/src/components/build/tabs/builder/BuilderServer.tsx
Normal file
46
frontend/src/components/build/tabs/builder/BuilderServer.tsx
Normal file
@@ -0,0 +1,46 @@
|
||||
import { Component } from "solid-js";
|
||||
import { useAppState } from "../../../../state/StateProvider";
|
||||
import { PermissionLevel } from "../../../../types";
|
||||
import { getId } from "../../../../util/helpers";
|
||||
import Flex from "../../../shared/layout/Flex";
|
||||
import Grid from "../../../shared/layout/Grid";
|
||||
import Selector from "../../../shared/menu/Selector";
|
||||
import { useConfig } from "../Provider";
|
||||
|
||||
const BuilderServer: Component<{}> = (p) => {
|
||||
const { servers, getPermissionOnServer } = useAppState();
|
||||
const { setBuild, server, userCanUpdate } = useConfig();
|
||||
const availableServers = () => {
|
||||
if (!servers.loaded()) return [];
|
||||
return servers
|
||||
.ids()!
|
||||
.filter((id) => {
|
||||
return getPermissionOnServer(id) === PermissionLevel.Update;
|
||||
});
|
||||
};
|
||||
return (
|
||||
<Flex
|
||||
class="config-item shadow"
|
||||
alignItems="center"
|
||||
justifyContent="space-between"
|
||||
>
|
||||
<h1>builder server</h1>
|
||||
<Selector
|
||||
targetClass="blue"
|
||||
selected={server()?.server ? getId(server()!.server) : "select server"}
|
||||
items={availableServers()}
|
||||
onSelect={(server_id) => setBuild("server_id", server_id)}
|
||||
itemMap={(server_id) =>
|
||||
server_id === "select server"
|
||||
? "select server"
|
||||
: servers.get(server_id)!.server.name
|
||||
}
|
||||
disabled={!userCanUpdate()}
|
||||
position="bottom right"
|
||||
useSearch
|
||||
/>
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
export default BuilderServer;
|
||||
60
frontend/src/components/build/tabs/builder/BuilderType.tsx
Normal file
60
frontend/src/components/build/tabs/builder/BuilderType.tsx
Normal file
@@ -0,0 +1,60 @@
|
||||
import { Component, Show } from "solid-js";
|
||||
import { useAppState } from "../../../../state/StateProvider";
|
||||
import Flex from "../../../shared/layout/Flex";
|
||||
import Grid from "../../../shared/layout/Grid";
|
||||
import { useConfig } from "../Provider";
|
||||
|
||||
const BuilderType: Component<{}> = (p) => {
|
||||
const { servers } = useAppState();
|
||||
const { build, setBuild, userCanUpdate } = useConfig();
|
||||
const builderType = () => {
|
||||
if (build.server_id) {
|
||||
return "server";
|
||||
} else if (build.aws_config) {
|
||||
return "aws";
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
};
|
||||
return (
|
||||
<Flex
|
||||
class="config-item shadow"
|
||||
alignItems="center"
|
||||
justifyContent="space-between"
|
||||
>
|
||||
<h1>builder type</h1>
|
||||
<Show when={userCanUpdate()} fallback={<h2>{builderType()}</h2>}>
|
||||
<Grid gap="0" gridTemplateColumns="1fr 1fr">
|
||||
<button
|
||||
class={builderType() === "server" ? "blue" : "grey"}
|
||||
style={{ width: "100%" }}
|
||||
onClick={() => {
|
||||
if (builderType() !== "server") {
|
||||
const server_id =
|
||||
servers.ids()?.length || 0 > 0
|
||||
? servers.ids()![0]
|
||||
: undefined;
|
||||
setBuild({ server_id, aws_config: undefined });
|
||||
}
|
||||
}}
|
||||
>
|
||||
server
|
||||
</button>
|
||||
<button
|
||||
class={builderType() === "aws" ? "blue" : "grey"}
|
||||
style={{ width: "100%" }}
|
||||
onClick={() => {
|
||||
if (builderType() !== "aws") {
|
||||
setBuild({ server_id: undefined, aws_config: {} });
|
||||
}
|
||||
}}
|
||||
>
|
||||
aws
|
||||
</button>
|
||||
</Grid>
|
||||
</Show>
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
export default BuilderType;
|
||||
@@ -1,12 +1,10 @@
|
||||
import { Component, createEffect, createSignal, Show } from "solid-js";
|
||||
import {
|
||||
combineClasses,
|
||||
parseDotEnvToEnvVars,
|
||||
parseEnvVarseToDotEnv,
|
||||
} from "../../../../util/helpers";
|
||||
import { useToggle } from "../../../../util/hooks";
|
||||
import Flex from "../../../shared/layout/Flex";
|
||||
import Grid from "../../../shared/layout/Grid";
|
||||
import CenterMenu from "../../../shared/menu/CenterMenu";
|
||||
import TextArea from "../../../shared/TextArea";
|
||||
import { useConfig } from "../Provider";
|
||||
@@ -14,24 +12,26 @@ import { useConfig } from "../Provider";
|
||||
const BuildArgs: Component<{}> = (p) => {
|
||||
const { build, userCanUpdate } = useConfig();
|
||||
return (
|
||||
<Grid class={combineClasses("config-item shadow")}>
|
||||
<Flex alignItems="center" justifyContent="space-between">
|
||||
<h1>build args</h1>
|
||||
<Flex alignItems="center" gap="0.2rem">
|
||||
<Show
|
||||
when={
|
||||
!build.docker_build_args?.build_args ||
|
||||
build.docker_build_args.build_args.length === 0
|
||||
}
|
||||
>
|
||||
<div>none</div>
|
||||
</Show>
|
||||
<Show when={userCanUpdate()}>
|
||||
<EditBuildArgs />
|
||||
</Show>
|
||||
</Flex>
|
||||
<Flex
|
||||
class="config-item shadow"
|
||||
alignItems="center"
|
||||
justifyContent="space-between"
|
||||
>
|
||||
<h1>build args</h1>
|
||||
<Flex alignItems="center" gap="0.2rem">
|
||||
<Show
|
||||
when={
|
||||
!build.docker_build_args?.build_args ||
|
||||
build.docker_build_args.build_args.length === 0
|
||||
}
|
||||
>
|
||||
<div>none</div>
|
||||
</Show>
|
||||
<Show when={userCanUpdate()}>
|
||||
<EditBuildArgs />
|
||||
</Show>
|
||||
</Flex>
|
||||
</Grid>
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -9,6 +9,8 @@ import { useConfig } from "../Provider";
|
||||
import Loading from "../../../shared/loading/Loading";
|
||||
import BuildArgs from "./BuildArgs";
|
||||
import Version from "./Version";
|
||||
import Repo from "./Repo";
|
||||
import ListenerUrl from "./ListenerUrl";
|
||||
|
||||
const BuildConfig: Component<{}> = (p) => {
|
||||
const { build, reset, save, userCanUpdate } = useConfig();
|
||||
@@ -17,9 +19,11 @@ const BuildConfig: Component<{}> = (p) => {
|
||||
<Grid class="config">
|
||||
<Grid class="config-items scroller">
|
||||
<Version />
|
||||
<Repo />
|
||||
<Docker />
|
||||
<BuildArgs />
|
||||
<CliBuild />
|
||||
<ListenerUrl />
|
||||
</Grid>
|
||||
<Show when={userCanUpdate() && build.updated}>
|
||||
<Show
|
||||
@@ -10,15 +10,28 @@ import Selector from "../../../shared/menu/Selector";
|
||||
import { useConfig } from "../Provider";
|
||||
|
||||
const Docker: Component<{}> = (p) => {
|
||||
const { aws_builder_config } = useAppState();
|
||||
const { build, setBuild, server, userCanUpdate } = useConfig();
|
||||
const [dockerAccounts, setDockerAccounts] = createSignal<string[]>();
|
||||
const [peripheryDockerAccounts, setPeripheryDockerAccounts] =
|
||||
createSignal<string[]>();
|
||||
createEffect(() => {
|
||||
if (server()?.status === ServerStatus.Ok) {
|
||||
client
|
||||
.get_server_docker_accounts(build.server_id)
|
||||
.then(setDockerAccounts);
|
||||
.get_server_docker_accounts(build.server_id!)
|
||||
.then(setPeripheryDockerAccounts);
|
||||
}
|
||||
});
|
||||
const dockerAccounts = () => {
|
||||
if (build.server_id) {
|
||||
return peripheryDockerAccounts() || [];
|
||||
} else if (build.aws_config) {
|
||||
const ami_id =
|
||||
build.aws_config?.ami_id || aws_builder_config()?.default_ami_id;
|
||||
return ami_id
|
||||
? aws_builder_config()?.available_ami_accounts![ami_id].docker || []
|
||||
: [];
|
||||
} else return [];
|
||||
};
|
||||
return (
|
||||
<Grid class={combineClasses("config-item shadow")}>
|
||||
<h1>docker build</h1> {/* checkbox here? */}
|
||||
@@ -62,7 +75,7 @@ const Docker: Component<{}> = (p) => {
|
||||
<Selector
|
||||
targetClass="blue"
|
||||
selected={build.docker_account || "none"}
|
||||
items={["none", ...dockerAccounts()!]}
|
||||
items={["none", ...dockerAccounts()]}
|
||||
onSelect={(account) => {
|
||||
setBuild(
|
||||
"docker_account",
|
||||
37
frontend/src/components/build/tabs/config/ListenerUrl.tsx
Normal file
37
frontend/src/components/build/tabs/config/ListenerUrl.tsx
Normal file
@@ -0,0 +1,37 @@
|
||||
import { Component, Show } from "solid-js";
|
||||
import { pushNotification, URL } from "../../../..";
|
||||
import { copyToClipboard, getId } from "../../../../util/helpers";
|
||||
import ConfirmButton from "../../../shared/ConfirmButton";
|
||||
import Icon from "../../../shared/Icon";
|
||||
import Flex from "../../../shared/layout/Flex";
|
||||
import Grid from "../../../shared/layout/Grid";
|
||||
import { useConfig } from "../Provider";
|
||||
|
||||
const ListenerUrl: Component<{}> = (p) => {
|
||||
const { build, userCanUpdate } = useConfig();
|
||||
const listenerUrl = () => `${URL}/api/listener/build/${getId(build)}`;
|
||||
return (
|
||||
<Show when={userCanUpdate()}>
|
||||
<Grid class="config-item shadow">
|
||||
<h1>webhook url</h1>
|
||||
<Flex justifyContent="space-between" alignItems="center">
|
||||
<div class="ellipsis" style={{ width: "250px" }}>
|
||||
{listenerUrl()}
|
||||
</div>
|
||||
<ConfirmButton
|
||||
class="blue"
|
||||
onFirstClick={() => {
|
||||
copyToClipboard(listenerUrl());
|
||||
pushNotification("good", "copied url to clipboard");
|
||||
}}
|
||||
confirm={<Icon type="check" />}
|
||||
>
|
||||
<Icon type="clipboard" />
|
||||
</ConfirmButton>
|
||||
</Flex>
|
||||
</Grid>
|
||||
</Show>
|
||||
);
|
||||
}
|
||||
|
||||
export default ListenerUrl;
|
||||
90
frontend/src/components/build/tabs/config/Repo.tsx
Normal file
90
frontend/src/components/build/tabs/config/Repo.tsx
Normal file
@@ -0,0 +1,90 @@
|
||||
import { Component, createEffect, createSignal, Show } from "solid-js";
|
||||
import Grid from "../../../shared/layout/Grid";
|
||||
import { useConfig } from "../Provider";
|
||||
import Flex from "../../../shared/layout/Flex";
|
||||
import Input from "../../../shared/Input";
|
||||
import { combineClasses } from "../../../../util/helpers";
|
||||
import { useAppState } from "../../../../state/StateProvider";
|
||||
import { client } from "../../../..";
|
||||
import { ServerStatus } from "../../../../types";
|
||||
import Selector from "../../../shared/menu/Selector";
|
||||
|
||||
const Repo: Component<{}> = (p) => {
|
||||
const { aws_builder_config } = useAppState();
|
||||
const { build, setBuild, server, userCanUpdate } = useConfig();
|
||||
const [peripheryGithubAccounts, setPeripheryGithubAccounts] =
|
||||
createSignal<string[]>();
|
||||
createEffect(() => {
|
||||
if (server()?.status === ServerStatus.Ok) {
|
||||
client
|
||||
.get_server_github_accounts(build.server_id!)
|
||||
.then(setPeripheryGithubAccounts);
|
||||
}
|
||||
});
|
||||
const githubAccounts = () => {
|
||||
if (build.server_id) {
|
||||
return peripheryGithubAccounts() || [];
|
||||
} else if (build.aws_config) {
|
||||
const ami_id =
|
||||
build.aws_config?.ami_id || aws_builder_config()?.default_ami_id;
|
||||
return ami_id
|
||||
? aws_builder_config()?.available_ami_accounts![ami_id].github || []
|
||||
: [];
|
||||
} else return [];
|
||||
};
|
||||
return (
|
||||
<Grid class={combineClasses("config-item shadow")}>
|
||||
<h1>repo config</h1>
|
||||
<Flex
|
||||
justifyContent={userCanUpdate() ? "space-between" : undefined}
|
||||
alignItems="center"
|
||||
style={{ "flex-wrap": "wrap" }}
|
||||
>
|
||||
<h2>repo: </h2>
|
||||
<Input
|
||||
placeholder="ie. solidjs/solid"
|
||||
value={build.repo || ""}
|
||||
onEdit={(value) => setBuild("repo", value)}
|
||||
disabled={!userCanUpdate()}
|
||||
/>
|
||||
</Flex>
|
||||
<Flex
|
||||
justifyContent={userCanUpdate() ? "space-between" : undefined}
|
||||
alignItems="center"
|
||||
style={{ "flex-wrap": "wrap" }}
|
||||
>
|
||||
<h2>branch: </h2>
|
||||
<Input
|
||||
placeholder="defaults to main"
|
||||
value={build.branch || (userCanUpdate() ? "" : "main")}
|
||||
onEdit={(value) => setBuild("branch", value)}
|
||||
disabled={!userCanUpdate()}
|
||||
/>
|
||||
</Flex>
|
||||
<Show when={githubAccounts()}>
|
||||
<Flex
|
||||
justifyContent={userCanUpdate() ? "space-between" : undefined}
|
||||
alignItems="center"
|
||||
style={{ "flex-wrap": "wrap" }}
|
||||
>
|
||||
<h2>github account: </h2>
|
||||
<Selector
|
||||
targetClass="blue"
|
||||
selected={build.github_account || "none"}
|
||||
items={["none", ...githubAccounts()]}
|
||||
onSelect={(account) => {
|
||||
setBuild(
|
||||
"github_account",
|
||||
account === "none" ? undefined : account
|
||||
);
|
||||
}}
|
||||
position="bottom right"
|
||||
disabled={!userCanUpdate()}
|
||||
/>
|
||||
</Flex>
|
||||
</Show>
|
||||
</Grid>
|
||||
);
|
||||
};
|
||||
|
||||
export default Repo;
|
||||
@@ -1,78 +0,0 @@
|
||||
import { Component, createEffect, createSignal } from "solid-js";
|
||||
import Grid from "../../../shared/layout/Grid";
|
||||
import { useConfig } from "../Provider";
|
||||
import Flex from "../../../shared/layout/Flex";
|
||||
import Input from "../../../shared/Input";
|
||||
import Selector from "../../../shared/menu/Selector";
|
||||
import { combineClasses } from "../../../../util/helpers";
|
||||
import { client } from "../../../..";
|
||||
import { ServerStatus } from "../../../../types";
|
||||
|
||||
const Git: Component<{}> = (p) => {
|
||||
const { build, setBuild, server, userCanUpdate } = useConfig();
|
||||
// const [githubAccounts, setGithubAccounts] = createSignal<string[]>();
|
||||
// createEffect(() => {
|
||||
// if (server()?.status === ServerStatus.Ok) {
|
||||
// client.get_server_github_accounts(build.server_id).then(setGithubAccounts);
|
||||
// }
|
||||
// });
|
||||
return (
|
||||
<Grid class={combineClasses("config-item shadow")}>
|
||||
<h1>github config</h1>
|
||||
<Flex
|
||||
justifyContent={userCanUpdate() ? "space-between" : undefined}
|
||||
alignItems="center"
|
||||
style={{ "flex-wrap": "wrap" }}
|
||||
>
|
||||
<h2>repo: </h2>
|
||||
<Input
|
||||
placeholder="ie. solidjs/solid"
|
||||
value={build.repo || ""}
|
||||
onEdit={(value) => setBuild("repo", value)}
|
||||
disabled={!userCanUpdate()}
|
||||
/>
|
||||
</Flex>
|
||||
<Flex
|
||||
justifyContent={userCanUpdate() ? "space-between" : undefined}
|
||||
alignItems="center"
|
||||
style={{ "flex-wrap": "wrap" }}
|
||||
>
|
||||
<h2>branch: </h2>
|
||||
<Input
|
||||
placeholder="defaults to main"
|
||||
value={build.branch || (userCanUpdate() ? "" : "main")}
|
||||
onEdit={(value) => setBuild("branch", value)}
|
||||
disabled={!userCanUpdate()}
|
||||
/>
|
||||
</Flex>
|
||||
<Flex
|
||||
justifyContent={userCanUpdate() ? "space-between" : undefined}
|
||||
alignItems="center"
|
||||
style={{ "flex-wrap": "wrap" }}
|
||||
>
|
||||
<h2>github account: </h2>
|
||||
{/* <Selector
|
||||
targetClass="blue"
|
||||
selected={build.github_account || "none"}
|
||||
items={["none", ...githubAccounts()!]}
|
||||
onSelect={(account) => {
|
||||
setBuild(
|
||||
"github_account",
|
||||
account === "none" ? undefined : account
|
||||
);
|
||||
}}
|
||||
position="bottom right"
|
||||
disabled={!userCanUpdate()}
|
||||
/> */}
|
||||
<Input
|
||||
placeholder="github username"
|
||||
value={build.github_account}
|
||||
onEdit={value => setBuild("github_account", value)}
|
||||
disabled={!userCanUpdate()}
|
||||
/>
|
||||
</Flex>
|
||||
</Grid>
|
||||
);
|
||||
};
|
||||
|
||||
export default Git;
|
||||
@@ -1,63 +0,0 @@
|
||||
import { Component } from "solid-js";
|
||||
import { combineClasses } from "../../../../util/helpers";
|
||||
import Input from "../../../shared/Input";
|
||||
import Grid from "../../../shared/layout/Grid";
|
||||
import { useConfig } from "../Provider";
|
||||
import Flex from "../../../shared/layout/Flex";
|
||||
|
||||
const OnClone: Component = () => {
|
||||
const { build, setBuild, userCanUpdate } = useConfig();
|
||||
return (
|
||||
<Grid class={combineClasses("config-item shadow")}>
|
||||
{/* <h1>on clone</h1>
|
||||
<Flex
|
||||
alignItems="center"
|
||||
justifyContent={userCanUpdate() ? "space-between" : undefined}
|
||||
style={{ "flex-wrap": "wrap" }}
|
||||
>
|
||||
<h2>path:</h2>
|
||||
<Input
|
||||
placeholder="relative to repo"
|
||||
value={build.on_clone?.path || ""}
|
||||
onEdit={(path) => {
|
||||
if (
|
||||
path.length === 0 &&
|
||||
(!build.on_clone ||
|
||||
!build.on_clone.command ||
|
||||
build.on_clone.command.length === 0)
|
||||
) {
|
||||
setBuild("on_clone", undefined);
|
||||
}
|
||||
setBuild("on_clone", { path });
|
||||
}}
|
||||
disabled={!userCanUpdate()}
|
||||
/>
|
||||
</Flex>
|
||||
<Flex
|
||||
alignItems="center"
|
||||
justifyContent={userCanUpdate() ? "space-between" : undefined}
|
||||
style={{ "flex-wrap": "wrap" }}
|
||||
>
|
||||
<h2>command:</h2>
|
||||
<Input
|
||||
placeholder="command"
|
||||
value={build.on_clone?.command || ""}
|
||||
onEdit={(command) => {
|
||||
if (
|
||||
command.length === 0 &&
|
||||
(!build.on_clone ||
|
||||
!build.on_clone.path ||
|
||||
build.on_clone.path.length === 0)
|
||||
) {
|
||||
setBuild("on_clone", undefined);
|
||||
}
|
||||
setBuild("on_clone", { command });
|
||||
}}
|
||||
disabled={!userCanUpdate()}
|
||||
/>
|
||||
</Flex> */}
|
||||
</Grid>
|
||||
);
|
||||
};
|
||||
|
||||
export default OnClone;
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useNavigate } from "@solidjs/router";
|
||||
import { createContext, ParentComponent, useContext } from "solid-js";
|
||||
import { createContext, createResource, ParentComponent, Resource, useContext } from "solid-js";
|
||||
import { useWindowKeyDown } from "../util/hooks";
|
||||
import {
|
||||
useBuilds,
|
||||
@@ -14,7 +14,8 @@ import {
|
||||
} from "./hooks";
|
||||
import connectToWs from "./ws";
|
||||
import { useUser } from "./UserProvider";
|
||||
import { PermissionLevel } from "../types";
|
||||
import { AwsBuilderConfig, PermissionLevel } from "../types";
|
||||
import { client } from "..";
|
||||
|
||||
export type State = {
|
||||
usernames: ReturnType<typeof useUsernames>;
|
||||
@@ -32,6 +33,7 @@ export type State = {
|
||||
procedures: ReturnType<typeof useProcedures>;
|
||||
getPermissionOnProcedure: (id: string) => PermissionLevel;
|
||||
updates: ReturnType<typeof useUpdates>;
|
||||
aws_builder_config: Resource<AwsBuilderConfig>;
|
||||
};
|
||||
|
||||
const context = createContext<
|
||||
@@ -51,6 +53,7 @@ export const AppStateProvider: ParentComponent = (p) => {
|
||||
const procedures = useProcedures();
|
||||
const deployments = useDeployments();
|
||||
const usernames = useUsernames();
|
||||
const [aws_builder_config] = createResource(() => client.get_aws_builder_defaults());
|
||||
const state: State = {
|
||||
usernames,
|
||||
servers,
|
||||
@@ -129,6 +132,7 @@ export const AppStateProvider: ParentComponent = (p) => {
|
||||
}
|
||||
},
|
||||
updates: useUpdates(),
|
||||
aws_builder_config,
|
||||
};
|
||||
|
||||
// createEffect(() => {
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
Generated by typeshare 1.0.0
|
||||
*/
|
||||
|
||||
export type AvailableAmiAccounts = Record<string, AmiAccounts>;
|
||||
|
||||
export type PermissionsMap = Record<string, PermissionLevel>;
|
||||
|
||||
export interface Action {
|
||||
@@ -74,6 +76,7 @@ export interface AwsBuilderConfig {
|
||||
default_ami_id: string;
|
||||
default_subnet_id: string;
|
||||
default_key_pair_name: string;
|
||||
available_ami_accounts?: AvailableAmiAccounts;
|
||||
default_region?: string;
|
||||
default_volume_gb?: number;
|
||||
default_instance_type?: string;
|
||||
@@ -81,6 +84,12 @@ export interface AwsBuilderConfig {
|
||||
default_assign_public_ip?: boolean;
|
||||
}
|
||||
|
||||
export interface AmiAccounts {
|
||||
name: string;
|
||||
github?: string[];
|
||||
docker?: string[];
|
||||
}
|
||||
|
||||
export interface Deployment {
|
||||
_id?: string;
|
||||
name: string;
|
||||
|
||||
@@ -112,6 +112,9 @@ pub struct AwsBuilderConfig {
|
||||
pub default_subnet_id: String,
|
||||
pub default_key_pair_name: String,
|
||||
|
||||
#[serde(default)]
|
||||
pub available_ami_accounts: AvailableAmiAccounts,
|
||||
|
||||
#[serde(default = "default_aws_region")]
|
||||
pub default_region: String,
|
||||
|
||||
@@ -140,6 +143,19 @@ fn default_instance_type() -> String {
|
||||
String::from("m5.2xlarge")
|
||||
}
|
||||
|
||||
#[typeshare]
|
||||
pub type AvailableAmiAccounts = HashMap<String, AmiAccounts>; // (ami_id, AmiAccounts)
|
||||
|
||||
#[typeshare]
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
|
||||
pub struct AmiAccounts {
|
||||
pub name: String,
|
||||
#[serde(default)]
|
||||
pub github: Vec<String>,
|
||||
#[serde(default)]
|
||||
pub docker: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct PeripheryConfig {
|
||||
#[serde(default = "default_periphery_port")]
|
||||
|
||||
Reference in New Issue
Block a user