fix: k8s types and modals

feat/k8s-scaffold
corpulent 3 years ago
parent 1c46e07bf3
commit 416a8fe40d

@ -0,0 +1,70 @@
import { ChevronDownIcon, ChevronUpIcon } from "@heroicons/react/outline";
import { styled } from "@mui/joy";
import IconButton from "@mui/joy/IconButton";
import { FunctionComponent, ReactElement, ReactNode } from "react";
import { useAccordionState } from "../../../../hooks";
export interface IAccordionProps {
id: string;
title: string;
defaultOpen?: boolean;
children: ReactNode;
}
const Root = styled("div")`
display: flex;
flex-direction: column;
`;
const Top = styled("div")`
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
&:hover {
cursor: pointer;
user-select: none;
}
`;
const Title = styled("h5")`
font-size: 0.85rem;
color: #374151;
font-weight: 700;
width: 100%;
text-align: left;
`;
const ExpandButton = styled(IconButton)`
border-radius: ${({ theme }) => theme.spacing(2)};
`;
const Bottom = styled("div")`
display: flex;
flex-direction: column;
row-gap: ${({ theme }) => theme.spacing(1)};
`;
const Accordion: FunctionComponent<IAccordionProps> = (
props: IAccordionProps
): ReactElement => {
const { id, defaultOpen = false, children, title } = props;
const { open, toggle } = useAccordionState(id, defaultOpen);
return (
<Root>
<Top onClick={toggle}>
<Title>{title}</Title>
<ExpandButton size="sm" variant="plain">
{open && <ChevronUpIcon className="h-5 w-5" />}
{!open && <ChevronDownIcon className="h-5 w-5" />}
</ExpandButton>
</Top>
{open && <Bottom>{children}</Bottom>}
</Root>
);
};
export default Accordion;

@ -0,0 +1,46 @@
import { useCallback } from "react";
import { CallbackFunction, IServiceNodeItem } from "../../../../types";
import { IEditServiceForm } from "../../../../types/docker-compose";
import {
getFinalValues,
getInitialValues,
tabs,
validationSchema
} from "./form-utils";
import { toaster } from "../../../../utils";
import FormModal from "../../../FormModal";
interface IModalServiceProps {
onHide: CallbackFunction;
onAddEndpoint: CallbackFunction;
}
const CreateServiceModal = (props: IModalServiceProps) => {
const { onHide, onAddEndpoint } = props;
const handleCreate = useCallback(
(finalValues: IServiceNodeItem, values: IEditServiceForm) => {
onHide();
onAddEndpoint(finalValues);
toaster(
`Created "${values.serviceName}" service successfully`,
"success"
);
},
[onAddEndpoint, onHide]
);
return (
<FormModal
title="Create service"
tabs={tabs}
getInitialValues={getInitialValues}
getFinalValues={getFinalValues}
validationSchema={validationSchema}
onHide={onHide}
onCreate={handleCreate}
/>
);
};
export default CreateServiceModal;

@ -0,0 +1,51 @@
import { useState, useEffect } from "react";
import type { CallbackFunction, IServiceNodeItem } from "../../../../types";
import type { IEditServiceForm } from "../../../../types/docker-compose";
import {
getInitialValues,
getFinalValues,
validationSchema,
tabs
} from "./form-utils";
import { toaster } from "../../../../utils";
import FormModal from "../../../FormModal";
export interface IModalServiceProps {
node: IServiceNodeItem;
onHide: CallbackFunction;
onUpdateEndpoint: CallbackFunction;
}
const ModalServiceEdit = (props: IModalServiceProps) => {
const { node, onHide, onUpdateEndpoint } = props;
const [selectedNode, setSelectedNode] = useState<IServiceNodeItem>();
useEffect(() => {
if (node) {
setSelectedNode(node);
}
}, [node]);
const handleUpdate = (
finalValues: IServiceNodeItem,
values: IEditServiceForm
) => {
onUpdateEndpoint(finalValues);
toaster(`Updated "${values.serviceName}" service successfully`, "success");
};
return (
<FormModal
title="Edit service"
tabs={tabs}
getInitialValues={getInitialValues}
getFinalValues={getFinalValues}
validationSchema={validationSchema}
onHide={onHide}
onCreate={handleUpdate}
selectedNode={selectedNode}
/>
);
};
export default ModalServiceEdit;

@ -0,0 +1,295 @@
import { styled } from "@mui/joy";
import { TFinalFormField } from "../../../../types";
import { SuperForm } from "../../../SuperForm";
const Root = styled("div")`
display: flex;
flex-direction: column;
row-gap: ${({ theme }) => theme.spacing(1)};
@media (max-width: 640px) {
row-gap: 0;
}
`;
const General = () => {
return (
<Root>
<SuperForm
fields={[
{
id: "row1",
type: "grid-row",
fields: [
{
id: "serviceName",
type: "text",
name: "serviceName",
label: "Service name",
required: true
}
]
},
{
id: "row2",
type: "grid-row",
fields: [
{
id: "imageName",
type: "text",
name: "imageName",
label: "Image name"
},
{
id: "imageTag",
type: "text",
name: "imageTag",
label: "Image tag"
}
]
},
{
id: "row3",
type: "grid-row",
fields: [
{
id: "containerName",
type: "text",
name: "containerName",
label: "Container name"
}
]
},
{
id: "row4",
type: "grid-row",
fields: [
{
id: "row4-column1",
type: "grid-column",
spans: [2, 3],
fields: [
{
id: "command",
type: "text",
name: "command",
label: "Command"
}
]
}
]
},
{
id: "row5",
type: "grid-row",
fields: [
{
id: "row5-column1",
type: "grid-column",
spans: [2, 3],
fields: [
{
id: "entrypoint",
type: "text",
name: "entrypoint",
label: "Entrypoint"
}
]
}
]
},
{
id: "row6",
type: "grid-row",
fields: [
{
id: "row6-column1",
type: "grid-column",
spans: [2, 3],
fields: [
{
id: "envFile",
type: "text",
name: "envFile",
label: "Env file"
}
]
}
]
},
{
id: "row7",
type: "grid-row",
fields: [
{
id: "row7-column1",
type: "grid-column",
spans: [2, 3],
fields: [
{
id: "workingDir",
type: "text",
name: "workingDir",
label: "Working directory"
}
]
}
]
},
{
id: "row8",
type: "grid-row",
fields: [
{
id: "row8-column1",
type: "grid-column",
spans: [2, 3],
fields: [
{
id: "restart",
type: "toggle",
name: "restart",
label: "Restart Policy",
options: [
{
value: "no",
text: "no"
},
{
value: "always",
text: "always"
},
{
value: "on-failure",
text: "on-failure"
},
{
value: "unless-stopped",
text: "unless-stopped"
}
]
}
]
}
]
},
{
id: "ports",
type: "records",
name: "ports",
title: "Ports",
defaultOpen: true,
fields: (index: number): TFinalFormField[] => [
{
id: `ports[${index}].hostPort`,
type: "text",
name: `ports[${index}].hostPort`,
placeholder: "Host port",
required: true
},
{
id: `ports[${index}].containerPort`,
type: "text",
name: `ports[${index}].containerPort`,
placeholder: "Container port"
},
{
id: `ports[${index}].protocol`,
type: "toggle",
name: `ports[${index}].protocol`,
label: "Protocol",
options: [
{
value: "tcp",
text: "TCP"
},
{
value: "udp",
text: "UDP"
}
]
}
],
newValue: {
hostPort: "",
containerPort: "",
protocol: ""
}
},
{
id: "dependsOn",
type: "records",
name: "dependsOn",
title: "Depends on",
fields: (index: number): TFinalFormField[] => [
{
id: `dependsOn[${index}]`,
type: "text",
name: `dependsOn[${index}]`,
placeholder: "Service name",
required: false
}
],
newValue: ""
},
{
id: "networks",
type: "records",
title: "Networks",
name: "networks",
fields: (index: number): TFinalFormField[] => [
{
id: `networks[${index}]`,
type: "text",
name: `networks[${index}]`,
placeholder: "Network name",
required: false
}
],
newValue: ""
},
{
id: "labels",
type: "records",
title: "Labels",
name: "labels",
fields: (index: number): TFinalFormField[] => [
{
id: `labels[${index}].key`,
type: "text",
name: `labels[${index}].key`,
placeholder: "Key",
required: true
},
{
id: `labels[${index}].value`,
type: "text",
name: `labels[${index}].value`,
placeholder: "Value",
required: true
}
],
newValue: { key: "", value: "" }
},
{
id: "profiles",
type: "records",
title: "Profiles",
name: "profiles",
fields: (index: number): TFinalFormField[] => [
{
id: `profiles[${index}]`,
name: `profiles[${index}]`,
placeholder: "Name",
required: true,
type: "text"
}
],
newValue: ""
}
]}
/>
</Root>
);
};
export default General;

@ -0,0 +1,55 @@
import type { IServiceNodeItem } from "../../../../types";
import type { IEditServiceForm } from "../../../../types/kubernetes";
import * as yup from "yup";
import General from "./General";
export const tabs = [
{
value: "general",
title: "General",
component: General
}
];
const initialValues: IEditServiceForm = {
serviceName: ""
};
export const validationSchema = yup.object({
serviceName: yup
.string()
.max(256, "Service name should be 256 characters or less")
.required("Service name is required")
});
export const getInitialValues = (node?: IServiceNodeItem): IEditServiceForm => {
if (!node) {
return {
...initialValues
};
}
const { canvasConfig } = node;
const { node_name = "" } = canvasConfig;
return {
serviceName: node_name
};
};
export const getFinalValues = (
values: IEditServiceForm,
previous?: IServiceNodeItem
): IServiceNodeItem => {
return {
key: previous?.key ?? "service",
type: "SERVICE",
position: previous?.position ?? { left: 0, top: 0 },
inputs: previous?.inputs ?? ["op_source"],
outputs: previous?.outputs ?? [],
canvasConfig: {
node_name: values.serviceName
},
serviceConfig: {}
};
};

@ -0,0 +1,441 @@
import { KeyValuePair, INodeItem, ICanvasConfig } from "../index";
export interface IVolumeTopLevel {
driver: string;
driver_opts: {
type: string;
o: string;
device: string;
};
external: boolean;
labels?: string[] | KeyValuePair;
name: string;
}
export interface IPAMConfig {
subnet?: string;
ip_range?: string;
gateway?: string;
aux_addresses?: Record<string, string>;
}
export interface IIPAM {
driver?: string;
config?: IPAMConfig[];
options?: Record<string, string>;
}
export interface INetworkTopLevel {
driver: string;
driver_opts: KeyValuePair;
attachable: boolean;
enable_ipv6: boolean;
ipam?: IIPAM;
internal: boolean;
labels?: string[] | KeyValuePair;
external: boolean;
name: string;
}
export interface IService {
build?: {
context: string;
dockerfile?: string;
args?: string[] | KeyValuePair;
ssh?: string[];
cache_from?: string[];
cache_to?: string[];
extra_hosts?: string[];
isolation?: string;
labels?: string[] | KeyValuePair;
shm_size?: string | number;
target?: string;
};
cpu_count: string | number;
cpu_percent: string | number;
cpu_shares: string | number;
cpu_period: string | number;
cpu_quota: string | number;
cpu_rt_runtime: string | number;
cpu_rt_period: string | number;
cpuset: number | number[];
cap_add: string[];
cap_drop: string[];
cgroup_parent: string;
command: string | string[];
configs:
| string[]
| {
[x: string]: {
source: string;
target: string;
uid: string;
gid: string;
mode: number;
};
};
container_name: string;
credential_spec: KeyValuePair;
depends_on:
| string[]
| {
[key: string]: {
condition: string;
};
};
deploy?: {
endpoint_mode?: "vip" | "dnsrr";
labels?: string[] | KeyValuePair;
mode?: "replicated" | "global";
placement?: {
constraints?: KeyValuePair[] | KeyValuePair;
preferences?: KeyValuePair[] | KeyValuePair;
};
replicas?: number;
resources?: {
limits?: {
cpus?: string;
memory?: string;
pids?: number;
};
reservations?: {
cpus?: string;
memory?: string;
devices?: { [key: string]: string | number | string[] }[];
};
};
restart_policy?: {
condition?: "none" | "on-failure" | "any";
delay?: string;
max_attempts?: number;
window?: string;
};
rollback_config?: {
parallelism?: number;
delay?: string;
failure_action?: "continue" | "pause";
monitor?: string;
max_failure_ratio?: string;
order?: "stop-first" | "start-first";
};
update_config?: {
parallelism?: number;
delay?: string;
failure_action?: "continue" | "pause";
monitor?: string;
max_failure_ratio?: string;
order?: "stop-first" | "start-first";
};
};
device_cgroup_rules: string[];
devices: string[];
dns: string | string[];
dns_opt: string[];
dns_search: string | string[];
domainname: string;
entrypoint: string | string[];
env_file: string | string[];
environment: string[] | KeyValuePair;
expose: string[];
extends: KeyValuePair;
external_links: string[];
extra_hosts: string[];
group_add: string[];
healthcheck: {
test: string[];
interval: string;
timeout: string;
retries: number;
start_period: string;
};
hostname: string;
image: string;
init: boolean;
ipc: string;
isolation: string;
labels: string[] | KeyValuePair;
links: string[];
logging: {
driver: string;
options: KeyValuePair;
};
network_mode: string;
networks:
| string[]
| {
[x: string]: {
aliases: string[];
ipv4_address: string;
ipv6_address: string;
link_local_ips: string[];
priority: number;
};
};
mac_address: string;
mem_swappiness: number;
memswap_limit: string | number;
oom_kill_disable: boolean;
oom_score_adj: number;
pid: string | number;
platform: string;
ports:
| string[]
| {
target: number;
host_ip: string;
published: string | number;
protocol: string;
mode: string;
};
privileged: boolean;
profiles?: string[];
pull_policy: string;
read_only: boolean;
restart: string;
runtime: string;
secrets:
| string[]
| {
source: string;
target: string;
uid: string;
gid: string;
mode: number;
};
security_opt: string[];
shm_size: string;
stdin_open: boolean;
stop_grace_period: string;
stop_signal: string;
storage_opt: {
size: string;
};
sysctls: string[] | KeyValuePair;
tmpfs: string | string[];
tty: boolean;
ulimits: {
nproc: number;
nofile: {
soft: number;
hard: number;
};
};
user: string;
userns_mode: string;
volumes:
| string[]
| {
type: string;
source: string;
target: string;
read_only: boolean;
bind: {
propagation: string;
create_host_path: boolean;
selinux: string;
};
volume: {
nocopy: boolean;
};
tmpfs: {
size: string | number;
};
consistency: string;
};
volumes_from: string[];
working_dir: string;
}
export interface IVolumeNodeItem extends INodeItem {
outputs: string[];
canvasConfig: ICanvasConfig;
volumeConfig: Partial<IVolumeTopLevel>;
}
export interface INetworkNodeItem extends INodeItem {
outputs: string[];
canvasConfig: ICanvasConfig;
networkConfig: Partial<INetworkTopLevel>;
}
export interface IProjectPayload {
name: string;
visibility: number;
project_type: number;
data: {
canvas: {
position: {
top: number;
left: number;
scale: number;
};
nodes: any;
connections: any;
networks: any;
};
};
}
export interface IGeneratePayload {
data: {
version: number;
networks: Record<string, Partial<INetworkTopLevel>>;
services: Record<string, Partial<IService>>;
volumes: Record<string, Partial<IVolumeTopLevel>>;
};
}
export interface IEditServiceForm {
build: {
context: string;
dockerfile: string;
arguments: {
key: string[];
value: string[];
}[];
sshAuthentications: {
id: string;
path: string;
}[];
cacheFrom: string[];
cacheTo: string[];
extraHosts: {
hostName: string;
ipAddress: string;
}[];
isolation: string;
labels: {
key: string[];
value: string[];
}[];
sharedMemorySize: string;
target: string;
};
command: string;
deploy: {
/**
* The default value for `mode` is `replicated`. However, we allow
* it to be empty. Thus, `mode` attribute can be pruned away
* if the user never assigned `mode` explicitly.
*/
mode: "" | "global" | "replicated";
/**
* The default value for `endpointMode` is platform dependent.
*/
endpointMode: "" | "vip" | "dnsrr";
replicas: string;
placement: {
constraints: {
key: string;
value: string;
}[];
preferences: {
key: string;
value: string;
}[];
};
resources: {
limits: {
cpus: string;
memory: string;
pids: string;
};
reservations: {
cpus: string;
memory: string;
};
};
restartPolicy: {
/**
* The default value for `condition` is `any`. However, we allow
* it to be empty. Thus, `deploy` attribute can be pruned away
* if the user never assigned `condition` explicitly.
*/
condition: "" | "none" | "on-failure" | "any";
delay: string;
maxAttempts: string;
window: string;
};
rollbackConfig: {
parallelism: string;
delay: string;
failureAction: "" | "continue" | "pause";
monitor: string;
maxFailureRatio: string;
order: "" | "stop-first" | "start-first";
};
updateConfig: {
parallelism: string;
delay: string;
failureAction: "" | "continue" | "pause";
monitor: string;
maxFailureRatio: string;
order: "" | "stop-first" | "start-first";
};
labels: {
key: string;
value: string;
}[];
};
entrypoint: string;
envFile: string;
serviceName: string;
imageName: string;
imageTag: string;
containerName: string;
networks: string[];
profiles: string[];
ports: {
hostPort: string;
containerPort: string;
protocol: "tcp" | "udp";
}[];
environmentVariables: {
key: string;
value: string;
}[];
restart: string;
volumes: {
name: string;
containerPath: string;
accessMode: string;
}[];
labels: {
key: string;
value: string;
}[];
dependsOn: string[];
workingDir: string;
}
export interface IEditVolumeForm {
entryName: string;
volumeName: string;
labels: {
key: string;
value: string;
}[];
}
export interface IEditNetworkForm {
entryName: string;
networkName: string;
driver: string;
configurations: {
subnet: string;
ipRange: string;
gateway: string;
auxAddresses: {
hostName: string;
ipAddress: string;
}[];
}[];
options: {
key: string;
value: string;
}[];
labels: {
key: string;
value: string;
}[];
}

@ -0,0 +1,30 @@
export interface IService {
container_name: string;
}
export interface IGeneratePayload {
data: {
services: Record<string, Partial<IService>>;
};
}
export interface IKubernetesProjectPayload {
name: string;
visibility: number;
project_type: number;
data: {
canvas: {
position: {
top: number;
left: number;
scale: number;
};
nodes: any;
connections: any;
};
};
}
export interface IEditServiceForm {
serviceName: string;
}
Loading…
Cancel
Save