begin cli v2

This commit is contained in:
mbecker20
2022-03-18 23:02:51 -07:00
parent 2db166ddb3
commit d64da60d4e
44 changed files with 472 additions and 391 deletions
-62
View File
@@ -1,62 +0,0 @@
import React, { ReactNode, useState } from "react";
import { Box, Newline, Text, useInput } from "ink";
import FinalConfig from "./components/FinalConfig";
import { useConfig, useSequence } from "./hooks";
import { Config } from "./types";
import Intro from "./components/Intro";
import Setup from "./components/Setup";
import Periphery from "./components/Periphery";
import Mongo from "./components/mongo/Mongo";
import { dockerNotInstalled } from "./monitor-cli";
import Docker from "./components/docker/Docker";
import Registry from "./components/registry/Registry";
const App = () => {
const [current, next, prev] = useSequence();
const [periphery, setPeriphery] = useState<boolean>();
const [installing, setInstalling] = useState(false);
const [config, setConfig] = useConfig<Config>({
mongoURL: "",
registryURL: "",
});
const corePages: ReactNode[] = [
<Mongo setConfig={setConfig} next={next} />,
<Registry setConfig={setConfig} next={next} />,
];
const peripheryPages: ReactNode[] = [];
const pages: ReactNode[] = [
<Intro next={next} />,
...(dockerNotInstalled ? [<Docker next={next} />] : []),
<Periphery setPeriphery={setPeriphery} next={next} />,
...(periphery === true ? peripheryPages : []),
...(periphery === false ? corePages : []),
<FinalConfig
periphery={periphery}
config={config}
onConfirm={() => {
next();
setInstalling(true);
}}
/>,
<Setup periphery={periphery} />,
];
useInput((_, key) => {
if (!installing && key.escape) prev();
});
return (
<Box flexDirection="column">
<Newline />
<Text color="blue" bold underline>
Monitor CLI
</Text>
<Newline />
{pages[current]}
</Box>
);
};
export default App;
+14
View File
@@ -0,0 +1,14 @@
import React from "react";
import { render } from "ink";
import { Box } from "ink";
import { checkDockerNotInstalled } from "./helpers/docker";
const App = () => {
return <Box></Box>;
};
export let dockerNotInstalled = true;
checkDockerNotInstalled().then((res) => {
dockerNotInstalled = res;
render(<App />);
});
-53
View File
@@ -1,53 +0,0 @@
import { Box, Newline, Text, useInput } from "ink";
import React from "react";
import { Config } from "../types";
const FinalConfig = (p: {
periphery: boolean | undefined;
config: Config;
onConfirm: () => void;
}) => {
useInput((_, key) => {
if (key.return) p.onConfirm();
});
if (p.periphery) {
return (
<Text>
press{" "}
<Text color="green" bold>
enter
</Text>{" "}
to start setup of the <Text color="red">monitor periphery</Text>. you
cannot go back after this begins.
</Text>
);
} else {
return (
<Box flexDirection="column">
<Text color="blue" bold>
your monitor core config
</Text>
<Newline />
<Box flexDirection="column" borderColor="green" borderStyle="round">
<Text color="green">
mongo url: <Text color="white">{p.config.mongoURL}</Text>
</Text>
<Text color="green">
registry url: <Text color="white">{p.config.registryURL}</Text>
</Text>
</Box>
<Newline />
<Text>
press{" "}
<Text color="green" bold>
enter
</Text>{" "}
to start setup of <Text color="cyan">monitor core</Text>. you cannot
go back after this begins.
</Text>
</Box>
);
}
};
export default FinalConfig;
-29
View File
@@ -1,29 +0,0 @@
import React from "react";
import { Box, Newline, Text, useInput } from "ink";
const Intro = (p: { next: () => void }) => {
useInput((_, key) => {
if (key.return) p.next();
});
return (
<Box flexDirection="column">
<Text>
This is a CLI to setup{" "}
<Text color="cyan" bold>
Monitor
</Text>
, a tool to manage application deployment.
</Text>
<Newline />
<Text>
press{" "}
<Text color="green" bold>
enter
</Text>{" "}
to continue.
</Text>
</Box>
);
};
export default Intro;
-28
View File
@@ -1,28 +0,0 @@
import React from "react";
import { Box, Newline, Text } from "ink";
import Selector from "./util/Selector";
import { SetConfig } from "../types";
const Periphery = (p: {
setPeriphery: (a: boolean) => void;
next: () => void;
}) => {
return (
<Box flexDirection="column">
<Text>
Are you setting up <Text color="cyan">monitor core</Text> or {" "}
<Text color="red">monitor periphery</Text>?
</Text>
<Newline />
<Selector
items={["core", "peripheral"]}
onSelect={(item) => {
p.setPeriphery(item === "periphery");
p.next();
}}
/>
</Box>
);
};
export default Periphery;
-18
View File
@@ -1,18 +0,0 @@
import React from "react";
import { Box, Text } from "ink";
const Setup = (p: { periphery: boolean | undefined }) => {
return (
<Box flexDirection="column">
<Text>
setting up{" "}
<Text color={p.periphery ? "red" : "cyan"}>
{p.periphery ? "a peripheral client..." : "monitor core"}
</Text>{" "}
{!p.periphery && "using the specified configuration..."}
</Text>
</Box>
);
};
export default Setup;
-40
View File
@@ -1,40 +0,0 @@
import React, { useEffect, useState } from "react";
import { Text } from "ink";
import { Next } from "../../types";
import YesNo from "../util/YesNo";
import InstallDocker from "./InstallDocker";
const Docker = ({ next }: { next: Next }) => {
const [installDocker, setInstallDocker] = useState<boolean>();
useEffect(() => {
if (installDocker === false) {
process.exit();
}
}, [installDocker]);
if (installDocker === undefined) {
return (
<YesNo
label={
<Text>
Docker does not appear to be installed. Would you like to install
Docker? This will begin the{" "}
<Text color="cyan" bold>
Docker Install Helper
</Text>{" "}
and is necessary to proceed.
</Text>
}
onSelect={(res) => {
setInstallDocker(res === "yes");
}}
direction="vertical"
/>
);
} else if (installDocker) {
return <InstallDocker next={next} />;
} else {
return <Text>Install docker and restart the CLI to proceed.</Text>;
}
};
export default Docker;
-132
View File
@@ -1,132 +0,0 @@
import React, { Fragment, useState } from "react";
import { Box, Newline, Text, useInput } from "ink";
import { Next } from "../../types";
import YesNo from "../util/YesNo";
import { installDockerUbuntu, InstallLog } from "../../helpers/docker";
const InstallDocker = ({ next }: { next: Next }) => {
const [stage, setStage] = useState<
| "sysCtlEnable"
| "confirm"
| "install"
| "installing"
| "finish"
| "error"
>("sysCtlEnable");
const [sysCtlEnable, setSysCtlEnable] = useState<"yes" | "no">();
const [logs, setLogs] = useState<InstallLog[]>([]);
useInput(async (_, key) => {
if (key.return) {
switch (stage) {
case "confirm":
const log = await installDockerUbuntu(
(log) => setLogs((logs) => [...logs, log]),
sysCtlEnable === "yes"
);
if (log) {
// there was some error
setLogs((logs) => [...logs, log])
setStage("error");
} else {
setStage("finish");
}
break;
case "finish":
next();
break;
case "error":
setSysCtlEnable(undefined);
setStage("sysCtlEnable");
break;
default:
break;
}
} else if (key.leftArrow) {
switch (stage) {
case "confirm":
setSysCtlEnable(undefined);
setStage("sysCtlEnable");
break;
default:
break;
}
}
});
return (
<Box flexDirection="column">
<Text color="cyan" bold>
Docker Install Helper
</Text>
<Newline />
{stage === "sysCtlEnable" && sysCtlEnable === undefined && (
<YesNo
label="start docker on system start (boot)?"
labelColor="white"
onSelect={(res) => {
setSysCtlEnable(res);
setStage("confirm");
}}
direction="vertical"
/>
)}
{sysCtlEnable !== undefined && (
<Text color="green">
start on boot: <Text color="white">{sysCtlEnable}</Text>
</Text>
)}
{stage === "confirm" && (
<Fragment>
<Newline />
<Text>
press <Text color="green">enter</Text> to install docker
</Text>
</Fragment>
)}
{(stage === "installing" || stage === "finish") && (
<Fragment>
{stage === "installing" && <Text color="cyan">installing...</Text>}
<Newline />
{logs.map(({ stage, log }) => {
<Fragment>
<Text color="cyan" bold>
{stage}
</Text>
<Text color="green">
command: <Text color="white">{log.command}</Text>
</Text>
{log.log.stdout ? (
<Text color="green">
stdout: <Text color="white">{log.log.stdout}</Text>
</Text>
) : undefined}
{log.log.stderr ? (
<Text color="red">
stderr: <Text color="white">{log.log.stderr}</Text>
</Text>
) : undefined}
<Newline />
</Fragment>;
})}
</Fragment>
)}
{stage === "finish" && (
<Text>
Docker has finished installing. Press <Text color="green">enter</Text>{" "}
to continue.
</Text>
)}
{stage === "error" && (
<Text>
There was an error during install. Press{" "}
<Text color="green">enter</Text> to try again.
</Text>
)}
</Box>
);
};
export default InstallDocker;
-105
View File
@@ -1,105 +0,0 @@
import { Box, Newline, Text, useInput } from "ink";
import React, { useState } from "react";
import { useBlinker } from "../../hooks";
import { SetConfig } from "../../types";
import YesNo from "../util/YesNo";
import SetupMongo from "./SetupMongo";
const Mongo = ({
setConfig,
next,
}: {
setConfig: SetConfig;
next: () => void;
}) => {
const [setup, setSetup] = useState<boolean>();
const [mongoURL, setMongoUrl] = useState("mongodb://127.0.0.1:27017/monitor");
const [confirm, setConfirm] = useState(false);
const blinker = useBlinker();
useInput((input, key) => {
if (setup === false) {
if (key.return) {
if (confirm) {
setConfig("mongoURL", mongoURL);
next();
} else {
setConfirm(true);
}
} else if (!confirm && key.delete) {
setMongoUrl(mongoURL.slice(0, mongoURL.length - 1));
} else if (key.leftArrow) {
setConfirm(false);
} else if (!confirm) {
setMongoUrl(mongoURL + input);
}
}
});
if (setup === undefined) {
return (
<YesNo
label={
<Text>
Do you need to set up mongo db locally? This will begin the{" "}
<Text color="cyan" bold>
Mongo Setup Helper
</Text>
.
</Text>
}
onYes={() => {
setSetup(true);
}}
onNo={() => {
setSetup(false);
}}
labelColor="white"
direction="vertical"
/>
);
} else if (setup) {
return (
<SetupMongo
blinker={blinker}
onFinished={(mongoURL) => {
setConfig("mongoURL", mongoURL);
next();
}}
/>
);
} else {
return (
<Box flexDirection="column">
<Box flexDirection="row">
<Text color="green">mongo url: </Text>
<Text>
{mongoURL}
{blinker && !confirm ? "|" : ""}
</Text>
</Box>
<Newline />
<Text color="gray">
note: remember to specify the database name in the url, ie
mongodb://localhost:27017<Text color="green">/monitor</Text>
</Text>
<Newline />
{confirm && (
<Text>
press{" "}
<Text color="green" bold>
enter
</Text>{" "}
to confirm mongo url or press the{" "}
<Text color="blue" bold>
left arrow
</Text>{" "}
on your keyboard to continue editing.
</Text>
)}
</Box>
);
}
};
export default Mongo;
-274
View File
@@ -1,274 +0,0 @@
import { CommandLogError } from "@monitor/types";
import { Box, Newline, Text, useInput } from "ink";
import React, { Fragment, useState } from "react";
import { startMongo } from "../../helpers/deploy/mongo";
import LabelledSelector from "../util/LabelledSelector";
import YesNo from "../util/YesNo";
const RESTART_MODES = ["no", "on-failure", "always", "unless-stopped"];
const SetupMongo = ({
onFinished,
blinker,
}: {
onFinished: (mongoURL: string) => void;
blinker: boolean;
}) => {
const [stage, setStage] = useState<
"name" | "port" | "volume" | "restart" | "confirm" | "finish"
>("name"); // 0: name, 1: port, 2: volume, 3: restart
const [name, setName] = useState("mongo-db");
const [port, setPort] = useState<string>();
const [volume, setVolume] = useState<string | false>();
const [restart, setRestart] = useState<string>();
const [running, setRunning] = useState(false);
const [result, setResult] = useState<CommandLogError>();
useInput((input, key) => {
if (key.return) {
switch (stage) {
case "name":
setStage("port");
setPort("27017");
break;
case "port":
if (!isNaN(Number(port))) {
setStage("volume");
}
break;
case "volume":
if (volume) {
setStage("restart");
}
break;
case "restart":
if (restart) {
setStage("confirm");
}
break;
case "confirm":
setRunning(true);
startMongo(name, Number(port!), volume!, restart!).then((res) => {
setResult(res);
setStage("finish");
});
break;
case "finish":
if (!result?.isError) {
onFinished(`mongodb://127.0.0.1:${port}/monitor`);
}
break;
default:
break;
}
} else if (key.leftArrow) {
switch (stage) {
case "port":
setPort(undefined);
setStage("name");
break;
case "volume":
setVolume(undefined);
setStage("port");
break;
case "restart":
setRestart(undefined);
if (volume === false) {
setVolume(undefined);
}
setStage("volume");
break;
case "confirm":
setRestart(undefined);
setStage("restart");
break;
default:
break;
}
} else if (key.delete) {
switch (stage) {
case "name":
setName(name.slice(0, Math.max(0, name.length - 1)));
break;
case "port":
if (port) setPort(port.slice(0, Math.max(0, port.length - 1)));
break;
case "volume":
if (volume)
setVolume(volume.slice(0, Math.max(0, volume.length - 1)));
break;
default:
break;
}
} else {
switch (stage) {
case "name":
setName(name + input);
break;
case "port":
const newPort = Number(port + input);
if (!isNaN(newPort)) {
setPort(newPort.toString());
}
break;
case "volume":
if (volume) setVolume(volume + input);
break;
default:
break;
}
}
});
return (
<Box flexDirection="column">
<Text color="cyan" bold>
Mongo Setup Helper
</Text>
<Newline />
<Text>
press your keyboard back arrow ({"<-"}) to go back to the previous field
</Text>
<Newline />
<Text color="green">
container name:{" "}
<Text color="white">
{name}
{stage === "name" && blinker ? "|" : ""}
</Text>
</Text>
{port && (
<Text color="green">
port:{" "}
<Text color="white">
{port}
{stage === "port" && blinker ? "|" : ""}
</Text>
</Text>
)}
{stage === "volume" && volume === undefined && (
<LabelledSelector
label="mount data on local filesystem:"
items={["yes", "no"]}
onSelect={(use) => {
if (use === "yes") {
setVolume("~/mongo");
} else {
setVolume(false);
setStage("restart");
}
}}
/>
)}
{(volume || volume === false) && (
<Text color="green">
{volume ? "mount folder: " : "mount: "}
<Text color="white">
{volume || "no"}
{stage === "volume" && blinker ? "|" : ""}
</Text>
</Text>
)}
{stage === "restart" && restart === undefined && (
<LabelledSelector
label="restart:"
items={RESTART_MODES}
onSelect={(mode) => {
setRestart(mode);
setStage("confirm");
}}
/>
)}
{restart !== undefined && (
<Text color="green">
restart: <Text color="white">{restart}</Text>
</Text>
)}
{/* {stage === "confirm" && (
<Box flexDirection="column">
<Newline />
{running ? (
<Text color="cyan">running...</Text>
) : (
<Text color="green">press enter to start mongo...</Text>
)}
</Box>
)} */}
{stage === "confirm" && (
<Fragment>
<Newline />
{running ? (
<Text color="cyan">running...</Text>
) : (
<Text>
press <Text color="green">enter</Text> to start mongo...
</Text>
)}
</Fragment>
)}
{result && (
<Fragment>
<Newline />
<Text>command: {result.command}</Text>
{result.log.stdout ? (
<Fragment>
<Newline />
<Text>stdout: {result.log.stdout}</Text>
</Fragment>
) : undefined}
{result.log.stderr ? (
<Fragment>
<Newline />
<Text>stderr: {result.log.stderr}</Text>
</Fragment>
) : undefined}
</Fragment>
)}
{stage === "finish" && result && !result.isError && (
<Text>
Success! Press{" "}
<Text color="green" bold>
enter
</Text>{" "}
to continue
</Text>
)}
{stage === "finish" && result && result.isError && (
<Fragment>
<Newline />
<YesNo
label="looks like that failed, would you like to retry?"
labelColor="white"
onYes={() => {
setPort(undefined);
setVolume(undefined);
setRestart(undefined);
setResult(undefined);
setStage("name");
}}
onNo={() => {
onFinished(`mongodb://127.0.0.1:${port}/monitor`);
}}
direction="vertical"
/>
</Fragment>
)}
</Box>
);
};
export default SetupMongo;
-100
View File
@@ -1,100 +0,0 @@
import { Box, Newline, Text, useInput } from "ink";
import React, { useState } from "react";
import { useBlinker } from "../../hooks";
import { SetConfig } from "../../types";
import YesNo from "../util/YesNo";
import SetupRegistry from "./SetupRegistry";
const Registry = ({
setConfig,
next,
}: {
setConfig: SetConfig;
next: () => void;
}) => {
const [setup, setSetup] = useState<boolean>();
const [registryURL, setRegistryURL] = useState("http://127.0.0.1:5000/");
const [confirm, setConfirm] = useState(false);
const blinker = useBlinker();
useInput((input, key) => {
if (setup === false) {
if (key.return) {
if (confirm) {
setConfig("registryURL", registryURL);
next();
} else {
setConfirm(true);
}
} else if (!confirm && key.delete) {
setRegistryURL(registryURL.slice(0, registryURL.length - 1));
} else if (key.leftArrow) {
setConfirm(false);
} else if (!confirm) {
setRegistryURL(registryURL + input);
}
}
});
if (setup === undefined) {
return (
<YesNo
label={
<Text>
Do you need to set up a docker registry locally? This will begin the{" "}
<Text color="cyan" bold>
Registry Setup Helper
</Text>
.
</Text>
}
onYes={() => {
setSetup(true);
}}
onNo={() => {
setSetup(false);
}}
labelColor="white"
direction="vertical"
/>
);
} else if (setup) {
return (
<SetupRegistry
blinker={blinker}
onFinished={(registryURL) => {
setConfig("registryURL", registryURL);
next();
}}
/>
);
} else {
return (
<Box flexDirection="column">
<Box flexDirection="row">
<Text color="green">registry url: </Text>
<Text>
{registryURL}
{blinker && !confirm ? "|" : ""}
</Text>
</Box>
<Newline />
{confirm && (
<Text>
press{" "}
<Text color="green" bold>
enter
</Text>{" "}
to confirm registry url or press the{" "}
<Text color="blue" bold>
left arrow
</Text>{" "}
on your keyboard to continue editing.
</Text>
)}
</Box>
);
}
};
export default Registry;
@@ -1,274 +0,0 @@
import { CommandLogError } from "@monitor/types";
import { Box, Newline, Text, useInput } from "ink";
import React, { Fragment, useState } from "react";
import { startRegistry } from "../../helpers/deploy/registry";
import LabelledSelector from "../util/LabelledSelector";
import YesNo from "../util/YesNo";
const RESTART_MODES = ["no", "on-failure", "always", "unless-stopped"];
const SetupRegistry = ({
onFinished,
blinker,
}: {
onFinished: (registryURL: string) => void;
blinker: boolean;
}) => {
const [stage, setStage] = useState<
"name" | "port" | "volume" | "restart" | "confirm" | "finish"
>("name"); // 0: name, 1: port, 2: volume, 3: restart
const [name, setName] = useState("monitor-registry");
const [port, setPort] = useState<string>();
const [volume, setVolume] = useState<string | false>();
const [restart, setRestart] = useState<string>();
const [running, setRunning] = useState(false);
const [result, setResult] = useState<CommandLogError>();
useInput((input, key) => {
if (key.return) {
switch (stage) {
case "name":
setStage("port");
setPort("5000");
break;
case "port":
if (!isNaN(Number(port))) {
setStage("volume");
}
break;
case "volume":
if (volume) {
setStage("restart");
}
break;
case "restart":
if (restart) {
setStage("confirm");
}
break;
case "confirm":
setRunning(true);
startRegistry(name, Number(port!), volume!, restart!).then((res) => {
setResult(res);
setStage("finish");
});
break;
case "finish":
if (!result?.isError) {
onFinished(`http://127.0.0.1:${port}/`);
}
break;
default:
break;
}
} else if (key.leftArrow) {
switch (stage) {
case "port":
setPort(undefined);
setStage("name");
break;
case "volume":
setVolume(undefined);
setStage("port");
break;
case "restart":
setRestart(undefined);
if (volume === false) {
setVolume(undefined);
}
setStage("volume");
break;
case "confirm":
setRestart(undefined);
setStage("restart");
break;
default:
break;
}
} else if (key.delete) {
switch (stage) {
case "name":
setName(name.slice(0, Math.max(0, name.length - 1)));
break;
case "port":
if (port) setPort(port.slice(0, Math.max(0, port.length - 1)));
break;
case "volume":
if (volume)
setVolume(volume.slice(0, Math.max(0, volume.length - 1)));
break;
default:
break;
}
} else {
switch (stage) {
case "name":
setName(name + input);
break;
case "port":
const newPort = Number(port + input);
if (!isNaN(newPort)) {
setPort(newPort.toString());
}
break;
case "volume":
if (volume) setVolume(volume + input);
break;
default:
break;
}
}
});
return (
<Box flexDirection="column">
<Text color="cyan" bold>
Registry Setup Helper
</Text>
<Newline />
<Text>
press your keyboard back arrow ({"<-"}) to go back to the previous field
</Text>
<Newline />
<Text color="green">
container name:{" "}
<Text color="white">
{name}
{stage === "name" && blinker ? "|" : ""}
</Text>
</Text>
{port && (
<Text color="green">
port:{" "}
<Text color="white">
{port}
{stage === "port" && blinker ? "|" : ""}
</Text>
</Text>
)}
{stage === "volume" && volume === undefined && (
<LabelledSelector
label="mount data on local filesystem:"
items={["yes", "no"]}
onSelect={(use) => {
if (use === "yes") {
setVolume("~/registry");
} else {
setVolume(false);
setStage("restart");
}
}}
/>
)}
{(volume || volume === false) && (
<Text color="green">
{volume ? "mount folder: " : "mount: "}
<Text color="white">
{volume || "no"}
{stage === "volume" && blinker ? "|" : ""}
</Text>
</Text>
)}
{stage === "restart" && restart === undefined && (
<LabelledSelector
label="restart:"
items={RESTART_MODES}
onSelect={(mode) => {
setRestart(mode);
setStage("confirm");
}}
/>
)}
{restart !== undefined && (
<Text color="green">
restart: <Text color="white">{restart}</Text>
</Text>
)}
{/* {stage === "confirm" && (
<Box flexDirection="column">
<Newline />
{running ? (
<Text color="cyan">running...</Text>
) : (
<Text color="green">press enter to start mongo...</Text>
)}
</Box>
)} */}
{stage === "confirm" && (
<Fragment>
<Newline />
{running ? (
<Text color="cyan">running...</Text>
) : (
<Text>
press <Text color="green">enter</Text> to start the registry...
</Text>
)}
</Fragment>
)}
{result && (
<Fragment>
<Newline />
<Text>command: {result.command}</Text>
{result.log.stdout ? (
<Fragment>
<Newline />
<Text>stdout: {result.log.stdout}</Text>
</Fragment>
) : undefined}
{result.log.stderr ? (
<Fragment>
<Newline />
<Text>stderr: {result.log.stderr}</Text>
</Fragment>
) : undefined}
</Fragment>
)}
{stage === "finish" && result && !result.isError && (
<Text>
Success! Press{" "}
<Text color="green" bold>
enter
</Text>{" "}
to continue
</Text>
)}
{stage === "finish" && result && result.isError && (
<Fragment>
<Newline />
<YesNo
label="looks like that failed, would you like to retry?"
labelColor="white"
onYes={() => {
setPort(undefined);
setVolume(undefined);
setRestart(undefined);
setResult(undefined);
setStage("name");
}}
onNo={() => {
onFinished(`http://127.0.0.1:${port}/`);
}}
direction="vertical"
/>
</Fragment>
)}
</Box>
);
};
export default SetupRegistry;
-2
View File
@@ -1,2 +0,0 @@
export const CORE_IMAGE = "mbecker2020/monitor-core";
export const PERIPHERY_IMAGE = "mbecker2020/monitor-periphery";
-13
View File
@@ -1,13 +0,0 @@
import { execute } from "../execute";
export async function startMongo(
name: string,
port: number,
volume: string | false,
restart: string
) {
const command = `docker run -d --name ${name} -p ${port}:27017${
volume ? ` -v ${volume}:/data/db` : ""
} --restart ${restart} mongo:latest`;
return await execute(command);
}
-16
View File
@@ -1,16 +0,0 @@
import { CORE_IMAGE } from "../../config";
import { Config } from "../../types";
import { execute } from "../execute";
export async function startMonitorCore({
monitorCore: {
},
mongo: { url: mongoURL },
registry: { url: registryURL },
}: Config) {
const command = `docker run -d --name monitor-core -e MONGO_URL=${mongoURL}${
registryURL ? ` -e REGISTRY_URL=${registryURL}` : ""
} ${CORE_IMAGE}`;
return await execute(command);
}
-13
View File
@@ -1,13 +0,0 @@
import { execute } from "../execute";
export async function startRegistry(
name: string,
port: number,
volume: string | false,
restart: string
) {
const command = `docker run -d --name ${name} -p ${port}:5000${
volume ? ` -v ${volume}:/var/lib/registry` : ""
} --restart ${restart} registry:2`;
return await execute(command);
}
+35 -32
View File
@@ -12,15 +12,15 @@ export async function installDockerUbuntu(
) {
const total = 6 + (systemCtlEnable ? 1 : 0);
const update = await execute("sudo apt-get update");
if (update.isError) return {
stage: "error updating system",
log: update
};
onCommandEnd({
if (update.isError)
return {
stage: "error updating system",
log: update,
};
onCommandEnd({
stage: `updated system (1 of ${total})`,
log: update,
});
const installDeps = await execute(`
sudo apt-get install \
@@ -29,38 +29,39 @@ export async function installDockerUbuntu(
gnupg \
lsb-release
`);
if (installDeps.isError) return {
stage: "error installing dependencies",
log: installDeps
};
onCommandEnd({
if (installDeps.isError)
return {
stage: "error installing dependencies",
log: installDeps,
};
onCommandEnd({
stage: `installed dependencies (2 of ${total})`,
log: installDeps,
});
const addKey = await execute(
"curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg"
);
if (addKey.isError) return {
stage: "error adding docker key",
log: addKey,
};
onCommandEnd({
if (addKey.isError)
return {
stage: "error adding docker key",
log: addKey,
};
onCommandEnd({
stage: `added docker key (3 of ${total})`,
log: addKey,
});
const setStableRepository = await execute(`
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu \
$(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
`);
if (setStableRepository.isError) return {
stage: "error adding stable repository",
log: setStableRepository,
};
if (setStableRepository.isError)
return {
stage: "error adding stable repository",
log: setStableRepository,
};
onCommandEnd({
stage: `set docker stable repository (4 of ${total})`,
log: setStableRepository,
@@ -69,10 +70,11 @@ export async function installDockerUbuntu(
const installDocker = await execute(
"sudo apt-get udpate && sudo apt-get install docker-ce docker-ce-cli containerd.io"
);
if (installDocker.isError) return {
stage: "error installing docker",
log: installDocker,
};
if (installDocker.isError)
return {
stage: "error installing docker",
log: installDocker,
};
onCommandEnd({
stage: `installed docker (5 of ${total})`,
log: installDocker,
@@ -95,10 +97,11 @@ export async function installDockerUbuntu(
const startOnBoot = await execute(
"sudo systemctl enable docker.service && sudo systemctl enable containerd.service"
);
if (startOnBoot.isError) return {
stage: "error configuring to start on boot",
log: startOnBoot,
};
if (startOnBoot.isError)
return {
stage: "error configuring to start on boot",
log: startOnBoot,
};
onCommandEnd({
stage: `configured to start on boot (7 of ${total})`,
log: startOnBoot,
@@ -109,6 +112,6 @@ export async function installDockerUbuntu(
}
export async function checkDockerNotInstalled() {
const res = await execute("docker ps");
return res.isError
const res = await execute("docker ps");
return res.isError;
}
-3
View File
@@ -1,3 +0,0 @@
export function toDashedName(name: string) {
return name.toLowerCase().replaceAll(" ", "-");
}
-44
View File
@@ -1,44 +0,0 @@
import { Deployment } from "@monitor/types";
import { model, Schema } from "mongoose";
export default function deploymentModel() {
const Conversion = new Schema({
local: String,
container: String,
});
const Volume = new Schema({
variable: String,
value: String,
});
const EnvironmentVar = new Schema({
variable: String,
value: String,
});
const schema = new Schema<Deployment>({
name: { type: String, unique: true, index: true },
containerName: { type: String, unique: true, index: true }, // for auto pull of frontend repo as well
owner: { type: String, index: true },
serverID: { type: String, index: true },
buildID: { type: String, index: true }, // if deploying a monitor build
/* to create docker run command */
image: String, // used if deploying an external image (from docker hub)
latest: Boolean, // if custom image, use this to add :latest
ports: [Conversion],
volumes: [Volume],
environment: [EnvironmentVar],
network: String,
restart: String,
postImage: String, // interpolated into run command after the image String
containerUser: String, // after -u in the run command
/* to manage repo for static frontend, mounted as a volume */
repo: String,
branch: { type: String, default: "master" },
accessToken: String,
containerMount: String, // the file path to mount repo on inside the container
});
return model("Deployment", schema)
}
-54
View File
@@ -1,54 +0,0 @@
import { Deployment } from "@monitor/types";
import mongoose from "mongoose";
import deploymentModel from "./deployment";
import serverModel from "./server";
import userModel from "./user";
export async function addInitialDocs(mongoURL: string, localMongo: boolean, localRegistry: boolean) {
await mongoose.connect(mongoURL);
const servers = serverModel();
const deployments = deploymentModel();
const users = userModel();
const coreServer = {
name: "Core Server",
address: "localhost",
passkey: "passkey",
enabled: true,
isCore: true,
};
const coreServerID = (await servers.create(coreServer)).toObject()._id;
const coreDeployment: Deployment = {
name: "Monitor Core",
containerName: "monitor-core",
image: "mbecker2020/monitor-core",
latest: true,
serverID: coreServerID,
owner: "admin",
};
deployments.create(coreDeployment);
if (localMongo) {
const mongoDeployment: Deployment = {
name: "Mongo DB",
containerName: "mongo-db",
image: "mongo",
latest: true,
owner: "admin",
};
deployments.create(mongoDeployment);
}
if (localRegistry) {
const registryDeployment: Deployment = {
name: "Registry",
containerName: "registry",
image: "registry:2",
serverID: coreServerID,
owner: "admin",
};
deployments.create(registryDeployment);
}
}
-14
View File
@@ -1,14 +0,0 @@
import { Server } from "@monitor/types";
import { model, Schema } from "mongoose";
export default function serverModel() {
const schema = new Schema<Server>({
name: { type: String, unique: true },
address: String,
passkey: String,
enabled: { type: Boolean, default: true },
isCore: Boolean,
});
return model("Server", schema);
}
-14
View File
@@ -1,14 +0,0 @@
import { User } from "@monitor/types";
import { Schema, model } from "mongoose";
export default function userModel() {
const schema = new Schema<User>({
username: { type: String, index: true, required: true },
permissions: { type: Number, default: 0 },
password: String,
avatar: String,
githubID: { type: Number, index: true },
});
return model("User", schema);
}
-37
View File
@@ -1,37 +0,0 @@
import { useCallback, useEffect, useState } from "react";
export function useSequence(): [
current: number,
next: () => void,
prev: () => void
] {
const [current, setCurrent] = useState(0);
const next = useCallback(() => {
setCurrent((current) => current + 1);
}, []);
const prev = useCallback(() => {
setCurrent((current) => Math.max(current - 1, 0));
}, []);
return [current, next, prev];
}
export function useConfig<T>(
init: T
): [T, (field: keyof T, val: number | string | boolean) => void] {
const [config, setConfig] = useState(init);
const set = useCallback((field: keyof T, val: string | number | boolean) => {
setConfig((config) => ({ ...config, [field]: val }));
}, []);
return [config, set];
}
export function useBlinker(interval = 750) {
const [on, setOn] = useState(false);
useEffect(() => {
const int = setInterval(() => {
setOn(on => !on);
}, interval)
return () => clearInterval(int);
}, []);
return on;
}
-31
View File
@@ -1,31 +0,0 @@
import React from "react";
import { render } from "ink";
// import meow from "meow";
import App from "./App";
import { checkDockerNotInstalled } from "./helpers/docker";
// const cli = meow(
// `
// Usage
// $ monitor-cli
// Options
// --name Your name
// Examples
// $ cli --name=Jane
// Hello, Jane
// `,
// {
// flags: {
// name: {
// type: "string",
// },
// },
// }
// );
export let dockerNotInstalled = true;
checkDockerNotInstalled().then((res) => {
dockerNotInstalled = res;
render(<App />);
});
+25 -13
View File
@@ -1,29 +1,41 @@
export type Config = {
monitorCore: {
name: string;
};
mongo: {
monitorCore?: {
name: string;
secretVolume: string; // to mount secrets.json into the container
hostNetwork?: boolean;
port?: number;
};
monitorPeriphery?: {
name: string;
hostNetwork?: boolean;
port?: number;
};
mongo?: {
url: string;
deploymentConfig?: {
// if mongo container already exists locally running on docker and should be added as a monitor managed deployment
};
startConfig?: {
// if this is attached, the cli will start container with this config and add
};
startConfig?: StartConfig;
};
registry: {
registry?: {
exists: boolean; // if this is false, dont use a registry and builds will be disabled
url?: string;
deploymentConfig?: {};
startConfig?: {};
startConfig?: StartConfig;
};
};
export type StartConfig = {
// if this is attached, the cli will start container with this config and add
name: string;
port: number;
volume: string | false;
restart: string;
};
export type SetConfig = (
domain: keyof Config,
field: keyof Config[keyof Config],
val: string | number | boolean
field: keyof Config,
val: string | number | boolean
) => void;
export type Next = () => void;