[release] version 0.5.0-unstable3
This commit is contained in:
parent
c670456d47
commit
3cbd88f4a6
|
@ -1,3 +1,11 @@
|
|||
## Version 0.5.0
|
||||
- Add Terminal to containers
|
||||
- Add Create Container
|
||||
- Add support for importing Docker Compose
|
||||
- Fixed 2 bugs with the smart shield, that made it too strict
|
||||
- Added more infoon the shield when blocking someone
|
||||
- Fixed home background image
|
||||
|
||||
## Version 0.4.3
|
||||
- Fix for exposing routes from the details page
|
||||
|
||||
|
|
|
@ -61,10 +61,6 @@ const preStyle = {
|
|||
marginTop: '10px',
|
||||
marginLeft: '0',
|
||||
marginRight: '0',
|
||||
display: 'block',
|
||||
textAlign: 'left',
|
||||
verticalAlign: 'baseline',
|
||||
opacity: '1',
|
||||
}
|
||||
|
||||
const DockerComposeImport = () => {
|
||||
|
@ -73,14 +69,22 @@ const DockerComposeImport = () => {
|
|||
const [openModal, setOpenModal] = useState(false);
|
||||
const [dockerCompose, setDockerCompose] = useState('');
|
||||
const [service, setService] = useState({});
|
||||
const [ymlError, setYmlError] = useState('');
|
||||
|
||||
useEffect(() => {
|
||||
if(dockerCompose === '') {
|
||||
return;
|
||||
}
|
||||
|
||||
let doc;
|
||||
let newService = {};
|
||||
let doc = yaml.load(dockerCompose);
|
||||
try {
|
||||
doc = yaml.load(dockerCompose);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
setYmlError(e.message);
|
||||
return;
|
||||
}
|
||||
|
||||
// convert to the proper format
|
||||
if(doc.services) {
|
||||
|
@ -110,7 +114,6 @@ const DockerComposeImport = () => {
|
|||
}
|
||||
|
||||
setService(doc);
|
||||
console.log(doc);
|
||||
}, [dockerCompose]);
|
||||
|
||||
return <>
|
||||
|
@ -135,13 +138,22 @@ const DockerComposeImport = () => {
|
|||
}}
|
||||
/>
|
||||
|
||||
<div style={{color: 'red'}}>
|
||||
{ymlError}
|
||||
</div>
|
||||
|
||||
<TextField
|
||||
multiline
|
||||
placeholder='Paste your docker-compose.yml here or use the file upload button.'
|
||||
fullWidth
|
||||
value={dockerCompose}
|
||||
onChange={(e) => setDockerCompose(e.target.value)}
|
||||
style={preStyle}
|
||||
sx={preStyle}
|
||||
InputProps={{
|
||||
sx: {
|
||||
color: '#EEE',
|
||||
}
|
||||
}}
|
||||
rows={20}></TextField>
|
||||
</Stack>}
|
||||
{step === 1 && <Stack spacing={2}>
|
||||
|
@ -154,6 +166,7 @@ const DockerComposeImport = () => {
|
|||
setOpenModal(false);
|
||||
setStep(0);
|
||||
setDockerCompose('');
|
||||
setYmlError('');
|
||||
}}>Close</Button>
|
||||
<Button onClick={() => {
|
||||
if(step === 0) {
|
||||
|
|
|
@ -18,6 +18,7 @@ import NetworkContainerSetup from './network';
|
|||
import VolumeContainerSetup from './volumes';
|
||||
import DockerTerminal from './terminal';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { smartDockerLogConcat, tryParseProgressLog } from '../../../utils/docker';
|
||||
|
||||
const preStyle = {
|
||||
backgroundColor: '#000',
|
||||
|
@ -56,6 +57,7 @@ const NewDockerService = ({service}) => {
|
|||
const [log, setLog] = React.useState([]);
|
||||
const [isDone, setIsDone] = React.useState(false);
|
||||
const [openModal, setOpenModal] = React.useState(false);
|
||||
const preRef = React.useRef(null);
|
||||
|
||||
React.useEffect(() => {
|
||||
// refreshContainer();
|
||||
|
@ -64,7 +66,8 @@ const NewDockerService = ({service}) => {
|
|||
const create = () => {
|
||||
setLog([])
|
||||
API.docker.createService(service, (newlog) => {
|
||||
setLog((old) => [...old, newlog]);
|
||||
setLog((old) => smartDockerLogConcat(old, newlog));
|
||||
preRef.current.scrollTop = preRef.current.scrollHeight;
|
||||
if (newlog.includes('[OPERATION SUCCEEDED]')) {
|
||||
setIsDone(true);
|
||||
}
|
||||
|
@ -100,19 +103,11 @@ const NewDockerService = ({service}) => {
|
|||
}}
|
||||
>Restart</Button>
|
||||
}
|
||||
<Link to={`/ui/servapps`}>
|
||||
<Button
|
||||
variant="outlined"
|
||||
color="primary"
|
||||
fullWidth
|
||||
startIcon={<AppstoreOutlined />}
|
||||
>Back to Servapps</Button>
|
||||
</Link>
|
||||
</Stack>}
|
||||
<pre style={preStyle}>
|
||||
<pre style={preStyle} ref={preRef}>
|
||||
{!log.length && JSON.stringify(service, false ,2)}
|
||||
{log.map((l) => {
|
||||
return <div>{l}</div>
|
||||
return <div>{tryParseProgressLog(l)}</div>
|
||||
})}
|
||||
</pre>
|
||||
</Stack>
|
||||
|
|
|
@ -205,7 +205,7 @@ const NewDockerServiceForm = () => {
|
|||
{
|
||||
title: 'Storage',
|
||||
disabled: maxTab < 2,
|
||||
children: <Stack spacing={2}><VolumeContainerSetup ontainer containerInfo={containerInfo} OnChange={(values) => {
|
||||
children: <Stack spacing={2}><VolumeContainerSetup newContainer containerInfo={containerInfo} OnChange={(values) => {
|
||||
console.log(values)
|
||||
const newValues = {
|
||||
...containerInfo,
|
||||
|
|
|
@ -12,6 +12,8 @@ const DockerTerminal = ({containerInfo, refresh}) => {
|
|||
const isInteractive = Config.Tty;
|
||||
const theme = useTheme();
|
||||
const isMobile = useMediaQuery((theme) => theme.breakpoints.down('sm'))
|
||||
const [history, setHistory] = useState([]);
|
||||
const [historyCursor, setHistoryCursor] = useState(0);
|
||||
|
||||
const [message, setMessage] = useState('');
|
||||
const [output, setOutput] = useState([
|
||||
|
@ -24,10 +26,31 @@ const DockerTerminal = ({containerInfo, refresh}) => {
|
|||
const [isConnected, setIsConnected] = useState(false);
|
||||
|
||||
const handleKeyDown = (e) => {
|
||||
if (e.key === 'Enter' && e.ctrlKey) {
|
||||
if (e.key === 'Enter') {
|
||||
// shift + enter for new line
|
||||
if (e.shiftKey) {
|
||||
return;
|
||||
}
|
||||
e.preventDefault();
|
||||
sendMessage();
|
||||
}
|
||||
if (e.key === 'ArrowUp') {
|
||||
e.preventDefault();
|
||||
if (historyCursor > 0) {
|
||||
setHistoryCursor(historyCursor - 1);
|
||||
setMessage(history[historyCursor - 1]);
|
||||
}
|
||||
}
|
||||
if (e.key === 'ArrowDown') {
|
||||
e.preventDefault();
|
||||
if (historyCursor < history.length - 1) {
|
||||
setHistoryCursor(historyCursor + 1);
|
||||
setMessage(history[historyCursor + 1]);
|
||||
} else {
|
||||
setHistoryCursor(history.length);
|
||||
setMessage('');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const makeInteractive = () => {
|
||||
|
@ -85,7 +108,9 @@ const DockerTerminal = ({containerInfo, refresh}) => {
|
|||
const sendMessage = () => {
|
||||
if (ws.current) {
|
||||
ws.current.send(message);
|
||||
setHistoryCursor(history.length + 1);
|
||||
setMessage('');
|
||||
setHistory((prevHistory) => [...prevHistory, message]);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
42
client/src/utils/docker.js
Normal file
42
client/src/utils/docker.js
Normal file
|
@ -0,0 +1,42 @@
|
|||
export const smartDockerLogConcat = (log, newLogRaw) => {
|
||||
const containsId = (log, id) => {
|
||||
try {
|
||||
const parsedLog = JSON.parse(log);
|
||||
return parsedLog.id === id;
|
||||
}
|
||||
catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const newLog = JSON.parse(newLogRaw);
|
||||
if (newLog.id) {
|
||||
if (log.find((l) => containsId(l, newLog.id))) {
|
||||
return log.map((l) => (containsId(l, newLog.id) ? newLogRaw : l));
|
||||
} else {
|
||||
return [...log, newLogRaw];
|
||||
}
|
||||
}
|
||||
return [...log, newLogRaw];
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
return [...log, newLogRaw];
|
||||
}
|
||||
}
|
||||
|
||||
export const tryParseProgressLog = (log) => {
|
||||
try {
|
||||
const parsedLog = JSON.parse(log);
|
||||
if (parsedLog.status && parsedLog.progress) {
|
||||
return `${parsedLog.id} ${parsedLog.status} ${parsedLog.progress}`
|
||||
} else if (parsedLog.status && parsedLog.progressDetail && parsedLog.progressDetail.current) {
|
||||
return `${parsedLog.id} ${parsedLog.status} ${parsedLog.progressDetail.current}/${parsedLog.progressDetail.total}`
|
||||
} else if (parsedLog.status) {
|
||||
return `${parsedLog.id} ${parsedLog.status} ${parsedLog.sha256 || ""}`
|
||||
}
|
||||
return log;
|
||||
} catch (e) {
|
||||
return log;
|
||||
}
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "cosmos-server",
|
||||
"version": "0.5.0-unstable2",
|
||||
"version": "0.5.0-unstable3",
|
||||
"description": "",
|
||||
"main": "test-server.js",
|
||||
"bugs": {
|
||||
|
|
|
@ -7,6 +7,9 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
"bufio"
|
||||
"strconv"
|
||||
"os"
|
||||
"os/user"
|
||||
"errors"
|
||||
"github.com/docker/go-connections/nat"
|
||||
"github.com/docker/docker/api/types/mount"
|
||||
|
@ -311,6 +314,7 @@ func CreateServiceRoute(w http.ResponseWriter, req *http.Request) {
|
|||
fmt.Fprintf(w, "Image %s pulled\n", container.Image)
|
||||
flusher.Flush()
|
||||
}
|
||||
|
||||
// Create containers
|
||||
for _, container := range serviceRequest.Services {
|
||||
utils.Log(fmt.Sprintf("Creating container %s...", container.Name))
|
||||
|
@ -362,6 +366,39 @@ func CreateServiceRoute(w http.ResponseWriter, req *http.Request) {
|
|||
}
|
||||
}
|
||||
|
||||
// Create missing folders for bind mounts
|
||||
for _, newmount := range container.Volumes {
|
||||
if newmount.Type == mount.TypeBind {
|
||||
if _, err := os.Stat(newmount.Source); os.IsNotExist(err) {
|
||||
err := os.MkdirAll(newmount.Source, 0755)
|
||||
if err != nil {
|
||||
utils.Error("CreateService: Unable to create directory for bind mount", err)
|
||||
fmt.Fprintf(w, "[ERROR] Unable to create directory for bind mount: "+err.Error())
|
||||
flusher.Flush()
|
||||
Rollback(rollbackActions, w, flusher)
|
||||
return
|
||||
}
|
||||
|
||||
// Change the ownership of the directory to the container.User
|
||||
userInfo, err := user.Lookup(container.User)
|
||||
if err != nil {
|
||||
utils.Error("CreateService: Unable to lookup user", err)
|
||||
fmt.Fprintf(w, "[ERROR] Unable to lookup user: "+err.Error())
|
||||
flusher.Flush()
|
||||
} else {
|
||||
uid, _ := strconv.Atoi(userInfo.Uid)
|
||||
gid, _ := strconv.Atoi(userInfo.Gid)
|
||||
err = os.Chown(newmount.Source, uid, gid)
|
||||
if err != nil {
|
||||
utils.Error("CreateService: Unable to change ownership of directory", err)
|
||||
fmt.Fprintf(w, "[ERROR] Unable to change ownership of directory: "+err.Error())
|
||||
flusher.Flush()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
hostConfig := &conttype.HostConfig{
|
||||
PortBindings: PortBindings,
|
||||
Mounts: container.Volumes,
|
||||
|
|
|
@ -4,6 +4,8 @@ import (
|
|||
"encoding/json"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/user"
|
||||
"strconv"
|
||||
|
||||
"github.com/azukaar/cosmos-server/src/utils"
|
||||
containerType "github.com/docker/docker/api/types/container"
|
||||
|
@ -83,6 +85,33 @@ func UpdateContainerRoute(w http.ResponseWriter, req *http.Request) {
|
|||
}
|
||||
}
|
||||
if(form.Volumes != nil) {
|
||||
// Create missing folders for bind mounts
|
||||
for _, newmount := range form.Volumes {
|
||||
if newmount.Type == mount.TypeBind {
|
||||
if _, err := os.Stat(newmount.Source); os.IsNotExist(err) {
|
||||
err := os.MkdirAll(newmount.Source, 0755)
|
||||
if err != nil {
|
||||
utils.Error("UpdateService: Unable to create directory for bind mount", err)
|
||||
utils.HTTPError(w, "Unable to create directory for bind mount: "+err.Error(), http.StatusInternalServerError, "DS004")
|
||||
return
|
||||
}
|
||||
|
||||
// Change the ownership of the directory to the container.User
|
||||
userInfo, err := user.Lookup(container.Config.User)
|
||||
if err != nil {
|
||||
utils.Error("UpdateService: Unable to lookup user", err)
|
||||
} else {
|
||||
uid, _ := strconv.Atoi(userInfo.Uid)
|
||||
gid, _ := strconv.Atoi(userInfo.Gid)
|
||||
err = os.Chown(newmount.Source, uid, gid)
|
||||
if err != nil {
|
||||
utils.Error("UpdateService: Unable to change ownership of directory", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
container.HostConfig.Mounts = form.Volumes
|
||||
container.HostConfig.Binds = []string{}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue