[release] v0.5.11
This commit is contained in:
parent
caddef01d1
commit
c97ebed936
|
@ -1,4 +1,9 @@
|
|||
## version 0.5.1 -> 0.5.7
|
||||
## versiom 0.5.11
|
||||
- Improve docker-compose import support for alternative syntaxes
|
||||
- Improve docker service creation when using force secure label (fixes few containers not liking restarting too fast when created)
|
||||
- Add toggle for using insecure HTTPS targets (fixes Unifi controller)
|
||||
|
||||
## version 0.5.1 -> 0.5.10
|
||||
- Add Wilcard certificates support
|
||||
- Auto switch to Mongo 4 if CPU has no ADX
|
||||
- Improve setup for certificates on new install
|
||||
|
|
|
@ -46,6 +46,7 @@ const RouteManagement = ({ routeConfig, routeNames, TargetContainer, noControls
|
|||
Target: routeConfig.Target,
|
||||
UseHost: routeConfig.UseHost,
|
||||
Host: routeConfig.Host,
|
||||
AcceptInsecureHTTPSTarget: routeConfig.AcceptInsecureHTTPSTarget === true,
|
||||
UsePathPrefix: routeConfig.UsePathPrefix,
|
||||
PathPrefix: routeConfig.PathPrefix,
|
||||
StripPathPrefix: routeConfig.StripPathPrefix,
|
||||
|
@ -167,6 +168,12 @@ const RouteManagement = ({ routeConfig, routeNames, TargetContainer, noControls
|
|||
/>
|
||||
}
|
||||
|
||||
{formik.values.Target.startsWith('https://') && <CosmosCheckbox
|
||||
name="AcceptInsecureHTTPSTarget"
|
||||
label="Accept Insecure HTTPS Target (not recommended)"
|
||||
formik={formik}
|
||||
/>}
|
||||
|
||||
<CosmosFormDivider title={'Source'} />
|
||||
|
||||
<Grid item xs={12}>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// material-ui
|
||||
import * as React from 'react';
|
||||
import { Alert, Button, Stack, Typography } from '@mui/material';
|
||||
import { WarningOutlined, PlusCircleOutlined, CopyOutlined, ExclamationCircleOutlined , SyncOutlined, UserOutlined, KeyOutlined, ArrowUpOutlined, FileZipOutlined } from '@ant-design/icons';
|
||||
import { WarningOutlined, PlusCircleOutlined, CopyOutlined, ExclamationCircleOutlined, SyncOutlined, UserOutlined, KeyOutlined, ArrowUpOutlined, FileZipOutlined } from '@ant-design/icons';
|
||||
import Table from '@mui/material/Table';
|
||||
import TableBody from '@mui/material/TableBody';
|
||||
import TableCell from '@mui/material/TableCell';
|
||||
|
@ -28,13 +28,13 @@ import NewDockerService from './newService';
|
|||
import yaml from 'js-yaml';
|
||||
|
||||
function checkIsOnline() {
|
||||
API.isOnline().then((res) => {
|
||||
window.location.reload();
|
||||
}).catch((err) => {
|
||||
setTimeout(() => {
|
||||
checkIsOnline();
|
||||
}, 1000);
|
||||
});
|
||||
API.isOnline().then((res) => {
|
||||
window.location.reload();
|
||||
}).catch((err) => {
|
||||
setTimeout(() => {
|
||||
checkIsOnline();
|
||||
}, 1000);
|
||||
});
|
||||
}
|
||||
|
||||
const preStyle = {
|
||||
|
@ -63,29 +63,24 @@ const preStyle = {
|
|||
marginRight: '0',
|
||||
}
|
||||
|
||||
const DockerComposeImport = ({refresh}) => {
|
||||
const [step, setStep] = useState(0);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [openModal, setOpenModal] = useState(false);
|
||||
const [dockerCompose, setDockerCompose] = useState('');
|
||||
const [service, setService] = useState({});
|
||||
const [ymlError, setYmlError] = useState('');
|
||||
const DockerComposeImport = ({ refresh }) => {
|
||||
const [step, setStep] = useState(0);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [openModal, setOpenModal] = useState(false);
|
||||
const [dockerCompose, setDockerCompose] = useState('');
|
||||
const [service, setService] = useState({});
|
||||
const [ymlError, setYmlError] = useState('');
|
||||
|
||||
useEffect(() => {
|
||||
if(dockerCompose === '') {
|
||||
return;
|
||||
}
|
||||
useEffect(() => {
|
||||
if (dockerCompose === '') {
|
||||
return;
|
||||
}
|
||||
|
||||
setYmlError('');
|
||||
let doc;
|
||||
let newService = {};
|
||||
try {
|
||||
doc = yaml.load(dockerCompose);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
setYmlError(e.message);
|
||||
return;
|
||||
}
|
||||
setYmlError('');
|
||||
let doc;
|
||||
let newService = {};
|
||||
try {
|
||||
doc = yaml.load(dockerCompose);
|
||||
|
||||
if (typeof doc === 'object' && doc !== null && Object.keys(doc).length > 0 &&
|
||||
!doc.services && !doc.networks && !doc.volumes) {
|
||||
|
@ -96,50 +91,64 @@ const DockerComposeImport = ({refresh}) => {
|
|||
|
||||
|
||||
// convert to the proper format
|
||||
if(doc.services) {
|
||||
if (doc.services) {
|
||||
Object.keys(doc.services).forEach((key) => {
|
||||
|
||||
// convert volumes
|
||||
if(doc.services[key].volumes) {
|
||||
let volumes = [];
|
||||
doc.services[key].volumes.forEach((volume) => {
|
||||
let volumeSplit = volume.split(':');
|
||||
let volumeObj = {
|
||||
Source: volumeSplit[0],
|
||||
Target: volumeSplit[1],
|
||||
Type: volume[0] === '/' ? 'bind' : 'volume',
|
||||
};
|
||||
volumes.push(volumeObj);
|
||||
});
|
||||
doc.services[key].volumes = volumes;
|
||||
if (doc.services[key].volumes) {
|
||||
if(Array.isArray(doc.services[key].volumes)) {
|
||||
let volumes = [];
|
||||
doc.services[key].volumes.forEach((volume) => {
|
||||
if (typeof volume === 'object') {
|
||||
volumes.push(volume);
|
||||
} else {
|
||||
let volumeSplit = volume.split(':');
|
||||
let volumeObj = {
|
||||
Source: volumeSplit[0],
|
||||
Target: volumeSplit[1],
|
||||
Type: volume[0] === '/' ? 'bind' : 'volume',
|
||||
};
|
||||
volumes.push(volumeObj);
|
||||
}
|
||||
});
|
||||
doc.services[key].volumes = volumes;
|
||||
}
|
||||
}
|
||||
|
||||
// convert expose
|
||||
if(doc.services[key].expose) {
|
||||
if (doc.services[key].expose) {
|
||||
doc.services[key].expose = doc.services[key].expose.map((port) => {
|
||||
return ''+port;
|
||||
return '' + port;
|
||||
})
|
||||
}
|
||||
|
||||
//convert user
|
||||
if(doc.services[key].user) {
|
||||
if (doc.services[key].user) {
|
||||
doc.services[key].user = '' + doc.services[key].user;
|
||||
}
|
||||
|
||||
// convert labels:
|
||||
if(doc.services[key].labels) {
|
||||
if(Array.isArray(doc.services[key].labels)) {
|
||||
if (doc.services[key].labels) {
|
||||
if (Array.isArray(doc.services[key].labels)) {
|
||||
let labels = {};
|
||||
doc.services[key].labels.forEach((label) => {
|
||||
const [key, value] = label.split('=');
|
||||
labels[''+key] = ''+value;
|
||||
labels['' + key] = '' + value;
|
||||
});
|
||||
doc.services[key].labels = labels;
|
||||
}
|
||||
if (typeof doc.services[key].labels == 'object') {
|
||||
let labels = {};
|
||||
Object.keys(doc.services[key].labels).forEach((keylabel) => {
|
||||
labels['' + keylabel] = '' + doc.services[key].labels[keylabel];
|
||||
});
|
||||
doc.services[key].labels = labels;
|
||||
}
|
||||
}
|
||||
|
||||
// convert environment
|
||||
if(doc.services[key].environment) {
|
||||
if(!Array.isArray(doc.services[key].environment)) {
|
||||
if (doc.services[key].environment) {
|
||||
if (!Array.isArray(doc.services[key].environment)) {
|
||||
let environment = [];
|
||||
Object.keys(doc.services[key].environment).forEach((keyenv) => {
|
||||
environment.push(keyenv + '=' + doc.services[key].environment[keyenv]);
|
||||
|
@ -149,100 +158,155 @@ const DockerComposeImport = ({refresh}) => {
|
|||
}
|
||||
|
||||
// convert network
|
||||
if(doc.services[key].networks) {
|
||||
if(Array.isArray(doc.services[key].networks)) {
|
||||
if (doc.services[key].networks) {
|
||||
if (Array.isArray(doc.services[key].networks)) {
|
||||
let networks = {};
|
||||
doc.services[key].networks.forEach((network) => {
|
||||
networks[''+network] = {};
|
||||
if (typeof network === 'object') {
|
||||
networks['' + network.name] = network;
|
||||
}
|
||||
else
|
||||
networks['' + network] = {};
|
||||
});
|
||||
doc.services[key].networks = networks;
|
||||
}
|
||||
}
|
||||
|
||||
// ensure container_name
|
||||
if(!doc.services[key].container_name) {
|
||||
if (!doc.services[key].container_name) {
|
||||
doc.services[key].container_name = key;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
setService(doc);
|
||||
}, [dockerCompose]);
|
||||
// convert networks
|
||||
if (doc.networks) {
|
||||
if (Array.isArray(doc.networks)) {
|
||||
let networks = {};
|
||||
doc.networks.forEach((network) => {
|
||||
if (typeof network === 'object') {
|
||||
networks['' + network.name] = network;
|
||||
}
|
||||
else
|
||||
networks['' + network] = {};
|
||||
});
|
||||
doc.networks = networks;
|
||||
} else {
|
||||
let networks = {};
|
||||
Object.keys(doc.networks).forEach((key) => {
|
||||
networks['' + key] = doc.networks[key] || {};
|
||||
});
|
||||
doc.networks = networks;
|
||||
}
|
||||
}
|
||||
|
||||
return <>
|
||||
<Dialog open={openModal} onClose={() => setOpenModal(false)}>
|
||||
<DialogTitle>Import Docker Compose</DialogTitle>
|
||||
<DialogContent>
|
||||
<DialogContentText>
|
||||
{step === 0 && <Stack spacing={2}>
|
||||
<Alert severity="warning" icon={<WarningOutlined />}>
|
||||
This is a highly experimental feature. It is recommended to use with caution.
|
||||
</Alert>
|
||||
// convert volumes
|
||||
if (doc.volumes) {
|
||||
if (Array.isArray(doc.volumes)) {
|
||||
let volumes = {};
|
||||
doc.volumes.forEach((volume) => {
|
||||
if(!volume) {
|
||||
volume = {};
|
||||
}
|
||||
if (typeof volume === 'object') {
|
||||
volumes['' + volume.name] = volume;
|
||||
}
|
||||
else
|
||||
volumes['' + volume] = {};
|
||||
});
|
||||
doc.volumes = volumes;
|
||||
} else {
|
||||
let volumes = {};
|
||||
Object.keys(doc.volumes).forEach((key) => {
|
||||
volumes['' + key] = doc.volumes[key] || {};
|
||||
});
|
||||
doc.volumes = volumes;
|
||||
}
|
||||
}
|
||||
|
||||
<UploadButtons
|
||||
accept='.yml,.yaml'
|
||||
OnChange={(e) => {
|
||||
const file = e.target.files[0];
|
||||
const reader = new FileReader();
|
||||
reader.onload = (e) => {
|
||||
setDockerCompose(e.target.result);
|
||||
};
|
||||
reader.readAsText(file);
|
||||
}}
|
||||
/>
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
setYmlError(e.message);
|
||||
return;
|
||||
}
|
||||
|
||||
<div style={{color: 'red'}}>
|
||||
{ymlError}
|
||||
</div>
|
||||
setService(doc);
|
||||
}, [dockerCompose]);
|
||||
|
||||
<TextField
|
||||
multiline
|
||||
placeholder='Paste your docker-compose.yml here or use the file upload button.'
|
||||
fullWidth
|
||||
value={dockerCompose}
|
||||
onChange={(e) => setDockerCompose(e.target.value)}
|
||||
sx={preStyle}
|
||||
InputProps={{
|
||||
sx: {
|
||||
color: '#EEE',
|
||||
}
|
||||
}}
|
||||
rows={20}></TextField>
|
||||
</Stack>}
|
||||
{step === 1 && <Stack spacing={2}>
|
||||
<NewDockerService service={service} refresh={refresh}/>
|
||||
</Stack>}
|
||||
</DialogContentText>
|
||||
</DialogContent>
|
||||
{!isLoading && <DialogActions>
|
||||
<Button onClick={() => {
|
||||
setOpenModal(false);
|
||||
setStep(0);
|
||||
setDockerCompose('');
|
||||
setYmlError('');
|
||||
}}>Close</Button>
|
||||
<Button onClick={() => {
|
||||
if(step === 0) {
|
||||
setStep(1);
|
||||
} else {
|
||||
setStep(0);
|
||||
}
|
||||
}}>
|
||||
{step === 0 && 'Next'}
|
||||
{step === 1 && 'Back'}
|
||||
</Button>
|
||||
</DialogActions>}
|
||||
</Dialog>
|
||||
return <>
|
||||
<Dialog open={openModal} onClose={() => setOpenModal(false)}>
|
||||
<DialogTitle>Import Docker Compose</DialogTitle>
|
||||
<DialogContent>
|
||||
<DialogContentText>
|
||||
{step === 0 && <Stack spacing={2}>
|
||||
<Alert severity="warning" icon={<WarningOutlined />}>
|
||||
This is a highly experimental feature. It is recommended to use with caution.
|
||||
</Alert>
|
||||
|
||||
<ResponsiveButton
|
||||
color="primary"
|
||||
onClick={() => setOpenModal(true)}
|
||||
variant="outlined"
|
||||
startIcon={<ArrowUpOutlined />}
|
||||
>
|
||||
Import Docker Compose
|
||||
</ResponsiveButton>
|
||||
</>;
|
||||
<UploadButtons
|
||||
accept='.yml,.yaml'
|
||||
OnChange={(e) => {
|
||||
const file = e.target.files[0];
|
||||
const reader = new FileReader();
|
||||
reader.onload = (e) => {
|
||||
setDockerCompose(e.target.result);
|
||||
};
|
||||
reader.readAsText(file);
|
||||
}}
|
||||
/>
|
||||
|
||||
<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)}
|
||||
sx={preStyle}
|
||||
InputProps={{
|
||||
sx: {
|
||||
color: '#EEE',
|
||||
}
|
||||
}}
|
||||
rows={20}></TextField>
|
||||
</Stack>}
|
||||
{step === 1 && <Stack spacing={2}>
|
||||
<NewDockerService service={service} refresh={refresh} />
|
||||
</Stack>}
|
||||
</DialogContentText>
|
||||
</DialogContent>
|
||||
{!isLoading && <DialogActions>
|
||||
<Button onClick={() => {
|
||||
setOpenModal(false);
|
||||
setStep(0);
|
||||
setDockerCompose('');
|
||||
setYmlError('');
|
||||
}}>Close</Button>
|
||||
<Button onClick={() => {
|
||||
if (step === 0) {
|
||||
setStep(1);
|
||||
} else {
|
||||
setStep(0);
|
||||
}
|
||||
}}>
|
||||
{step === 0 && 'Next'}
|
||||
{step === 1 && 'Back'}
|
||||
</Button>
|
||||
</DialogActions>}
|
||||
</Dialog>
|
||||
|
||||
<ResponsiveButton
|
||||
color="primary"
|
||||
onClick={() => setOpenModal(true)}
|
||||
variant="outlined"
|
||||
startIcon={<ArrowUpOutlined />}
|
||||
>
|
||||
Import Docker Compose
|
||||
</ResponsiveButton>
|
||||
</>;
|
||||
};
|
||||
|
||||
export default DockerComposeImport;
|
||||
|
|
4
package-lock.json
generated
4
package-lock.json
generated
|
@ -1,12 +1,12 @@
|
|||
{
|
||||
"name": "cosmos-server",
|
||||
"version": "0.5.0-unstable",
|
||||
"version": "0.5.10",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "cosmos-server",
|
||||
"version": "0.5.0-unstable",
|
||||
"version": "0.5.10",
|
||||
"dependencies": {
|
||||
"@ant-design/colors": "^6.0.0",
|
||||
"@ant-design/icons": "^4.7.0",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "cosmos-server",
|
||||
"version": "0.5.10",
|
||||
"version": "0.5.11",
|
||||
"description": "",
|
||||
"main": "test-server.js",
|
||||
"bugs": {
|
||||
|
@ -56,10 +56,10 @@
|
|||
"client": "vite",
|
||||
"client-build": "vite build --base=/ui/",
|
||||
"start": "env COSMOS_HOSTNAME=localhost CONFIG_FILE=./config_dev.json EZ=UTC build/cosmos",
|
||||
"build": " sh build.sh",
|
||||
"build": "sh build.sh",
|
||||
"dev": "npm run build && npm run start",
|
||||
"dockerdevbuild": "sh build.sh && npm run client-build && docker build --tag cosmos-dev .",
|
||||
"dockerdevrun": "docker stop cosmos-dev; docker rm cosmos-dev; docker run -d -p 80:80 -p 443:443 -e DOCKER_HOST=tcp://host.docker.internal:2375 -e COSMOS_MONGODB=$MONGODB -e COSMOS_LOG_LEVEL=DEBUG --restart=unless-stopped -h cosmos-dev --name cosmos-dev cosmos-dev",
|
||||
"dockerdevbuild": "sh build.sh && docker build --tag cosmos-dev .",
|
||||
"dockerdevrun": "docker stop cosmos-dev; docker rm cosmos-dev; docker run -d -p 80:80 -p 443:443 -e DOCKER_HOST=tcp://host.docker.internal:2375 -e COSMOS_MONGODB=$MONGODB -e COSMOS_LOG_LEVEL=DEBUG -v /:/mnt/host --restart=unless-stopped -h cosmos-dev --name cosmos-dev cosmos-dev",
|
||||
"dockerdev": "npm run dockerdevbuild && npm run dockerdevrun",
|
||||
"demo": "vite build --base=/ui/ --mode demo",
|
||||
"devdemo": "vite --mode demo"
|
||||
|
|
|
@ -110,7 +110,7 @@ Authentication is very hard (how do you check the password match? What encryptio
|
|||
Installation is simple using Docker:
|
||||
|
||||
```
|
||||
docker run -d -p 80:80 -p 443:443 --name cosmos-server -h cosmos-server --restart=always -v /var/run/docker.sock:/var/run/docker.sock -v /var/lib/cosmos:/config azukaar/cosmos-server:latest
|
||||
docker run -d -p 80:80 -p 443:443 --name cosmos-server -h cosmos-server --restart=always -v /var/run/docker.sock:/var/run/docker.sock -v /:/mnt/host -v /var/lib/cosmos:/config azukaar/cosmos-server:latest
|
||||
```
|
||||
|
||||
Once installed, simply go to `http://your-server-ip` and follow the instructions of the setup wizard.
|
||||
|
|
|
@ -22,6 +22,12 @@ import (
|
|||
"github.com/azukaar/cosmos-server/src/utils"
|
||||
)
|
||||
|
||||
type ContainerCreateRequestServiceNetwork struct {
|
||||
Aliases []string `json:"aliases,omitempty"`
|
||||
IPV4Address string `json:"ipv4_address,omitempty"`
|
||||
IPV6Address string `json:"ipv6_address,omitempty"`
|
||||
}
|
||||
|
||||
type ContainerCreateRequestContainer struct {
|
||||
Name string `json:"container_name"`
|
||||
Image string `json:"image"`
|
||||
|
@ -29,12 +35,8 @@ type ContainerCreateRequestContainer struct {
|
|||
Labels map[string]string `json:"labels"`
|
||||
Ports []string `json:"ports"`
|
||||
Volumes []mount.Mount `json:"volumes"`
|
||||
Networks map[string]struct {
|
||||
Aliases []string `json:"aliases,omitempty"`
|
||||
IPV4Address string `json:"ipv4_address,omitempty"`
|
||||
IPV6Address string `json:"ipv6_address,omitempty"`
|
||||
} `json:"networks"`
|
||||
Routes []utils.ProxyRouteConfig `json:"routes"`
|
||||
Networks map[string]ContainerCreateRequestServiceNetwork `json:"networks"`
|
||||
Routes []utils.ProxyRouteConfig `json:"routes"`
|
||||
|
||||
RestartPolicy string `json:"restart,omitempty"`
|
||||
Devices []string `json:"devices"`
|
||||
|
@ -100,7 +102,7 @@ type ContainerCreateRequestNetwork struct {
|
|||
|
||||
type DockerServiceCreateRequest struct {
|
||||
Services map[string]ContainerCreateRequestContainer `json:"services"`
|
||||
Volumes []ContainerCreateRequestVolume `json:"volumes"`
|
||||
Volumes map[string]ContainerCreateRequestVolume `json:"volumes"`
|
||||
Networks map[string]ContainerCreateRequestNetwork `json:"networks"`
|
||||
}
|
||||
|
||||
|
@ -137,6 +139,9 @@ func Rollback(actions []DockerServiceCreateRollback , w http.ResponseWriter, flu
|
|||
flusher.Flush()
|
||||
}
|
||||
case "network":
|
||||
if os.Getenv("HOSTNAME") != "" {
|
||||
DockerClient.NetworkDisconnect(DockerContext, action.Name, os.Getenv("HOSTNAME"), true)
|
||||
}
|
||||
err := DockerClient.NetworkRemove(DockerContext, action.Name)
|
||||
if err != nil {
|
||||
utils.Error("Rollback: Network", err)
|
||||
|
@ -220,6 +225,48 @@ func CreateService(w http.ResponseWriter, req *http.Request, serviceRequest Dock
|
|||
var rollbackActions []DockerServiceCreateRollback
|
||||
var err error
|
||||
|
||||
// check if services have the cosmos-force-network-secured label
|
||||
for serviceName, service := range serviceRequest.Services {
|
||||
utils.Log(fmt.Sprintf("Checking service %s...", serviceName))
|
||||
fmt.Fprintf(w, "Checking service %s...\n", serviceName)
|
||||
flusher.Flush()
|
||||
|
||||
if service.Labels["cosmos-force-network-secured"] == "true" {
|
||||
utils.Log(fmt.Sprintf("Forcing secure %s...", serviceName))
|
||||
fmt.Fprintf(w, "Forcing secure %s...\n", serviceName)
|
||||
flusher.Flush()
|
||||
|
||||
newNetwork, errNC := CreateCosmosNetwork()
|
||||
if errNC != nil {
|
||||
utils.Error("CreateService: Network", err)
|
||||
fmt.Fprintf(w, "[ERROR] Network %s cant be created\n", newNetwork)
|
||||
flusher.Flush()
|
||||
Rollback(rollbackActions, w, flusher)
|
||||
return err
|
||||
}
|
||||
|
||||
service.Labels["cosmos-network-name"] = newNetwork
|
||||
|
||||
AttachNetworkToCosmos(newNetwork)
|
||||
|
||||
if service.Networks == nil {
|
||||
service.Networks = make(map[string]ContainerCreateRequestServiceNetwork)
|
||||
}
|
||||
|
||||
service.Networks[newNetwork] = ContainerCreateRequestServiceNetwork{}
|
||||
|
||||
rollbackActions = append(rollbackActions, DockerServiceCreateRollback{
|
||||
Action: "remove",
|
||||
Type: "network",
|
||||
Name: newNetwork,
|
||||
})
|
||||
|
||||
utils.Log(fmt.Sprintf("Created secure network %s", newNetwork))
|
||||
fmt.Fprintf(w, "Created secure network %s\n", newNetwork)
|
||||
flusher.Flush()
|
||||
}
|
||||
}
|
||||
|
||||
// Create networks
|
||||
for networkToCreateName, networkToCreate := range serviceRequest.Networks {
|
||||
utils.Log(fmt.Sprintf("Creating network %s...", networkToCreateName))
|
||||
|
@ -391,11 +438,32 @@ func CreateService(w http.ResponseWriter, req *http.Request, serviceRequest Dock
|
|||
// 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)
|
||||
newSource := newmount.Source
|
||||
|
||||
if os.Getenv("HOSTNAME") != "" {
|
||||
if _, err := os.Stat("/mnt/host"); os.IsNotExist(err) {
|
||||
utils.Error("CreateService: Unable to create directory for bind mount in the host directory. Please mount the host / in Cosmos with -v /:/mnt/host to enable folder creations, or create the bind folder yourself", err)
|
||||
fmt.Fprintf(w, "[ERROR] Unable to create directory for bind mount in the host directory. Please mount the host / in Cosmos with -v /:/mnt/host to enable folder creations, or create the bind folder yourself: "+err.Error())
|
||||
flusher.Flush()
|
||||
Rollback(rollbackActions, w, flusher)
|
||||
return err
|
||||
}
|
||||
newSource = "/mnt/host" + newSource
|
||||
}
|
||||
|
||||
utils.Log(fmt.Sprintf("Checking directory %s for bind mount", newSource))
|
||||
fmt.Fprintf(w, "Checking directory %s for bind mount\n", newSource)
|
||||
flusher.Flush()
|
||||
|
||||
if _, err := os.Stat(newSource); os.IsNotExist(err) {
|
||||
utils.Log(fmt.Sprintf("Not found. Creating directory %s for bind mount", newSource))
|
||||
fmt.Fprintf(w, "Not found. Creating directory %s for bind mount\n", newSource)
|
||||
flusher.Flush()
|
||||
|
||||
err := os.MkdirAll(newSource, 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())
|
||||
utils.Error("CreateService: Unable to create directory for bind mount. Make sure parent directories exist, and that Cosmos has permissions to create directories in the host directory", err)
|
||||
fmt.Fprintf(w, "[ERROR] Unable to create directory for bind mount. Make sure parent directories exist, and that Cosmos has permissions to create directories in the host directory for bind mount: "+err.Error())
|
||||
flusher.Flush()
|
||||
Rollback(rollbackActions, w, flusher)
|
||||
return err
|
||||
|
@ -411,7 +479,7 @@ func CreateService(w http.ResponseWriter, req *http.Request, serviceRequest Dock
|
|||
} else {
|
||||
uid, _ := strconv.Atoi(userInfo.Uid)
|
||||
gid, _ := strconv.Atoi(userInfo.Gid)
|
||||
err = os.Chown(newmount.Source, uid, gid)
|
||||
err = os.Chown(newSource, 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())
|
||||
|
|
|
@ -4,8 +4,6 @@ import (
|
|||
"encoding/json"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/user"
|
||||
"strconv"
|
||||
|
||||
"github.com/azukaar/cosmos-server/src/utils"
|
||||
containerType "github.com/docker/docker/api/types/container"
|
||||
|
@ -86,33 +84,6 @@ 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{}
|
||||
}
|
||||
|
|
|
@ -5,12 +5,17 @@ import (
|
|||
"errors"
|
||||
"time"
|
||||
"bufio"
|
||||
"os"
|
||||
"os/user"
|
||||
"fmt"
|
||||
"strings"
|
||||
"strconv"
|
||||
"github.com/azukaar/cosmos-server/src/utils"
|
||||
|
||||
"github.com/docker/docker/client"
|
||||
// natting "github.com/docker/go-connections/nat"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
mountType "github.com/docker/docker/api/types/mount"
|
||||
"github.com/docker/docker/api/types"
|
||||
)
|
||||
|
||||
|
@ -111,6 +116,49 @@ func EditContainer(oldContainerID string, newConfig types.ContainerJSON, noLock
|
|||
oldContainer := newConfig
|
||||
|
||||
if(oldContainerID != "") {
|
||||
// create missing folders
|
||||
|
||||
for _, newmount := range newConfig.HostConfig.Mounts {
|
||||
if newmount.Type == mountType.TypeBind {
|
||||
newSource := newmount.Source
|
||||
|
||||
if os.Getenv("HOSTNAME") != "" {
|
||||
if _, err := os.Stat("/mnt/host"); os.IsNotExist(err) {
|
||||
utils.Error("EditContainer: Unable to create directory for bind mount in the host directory. Please mount the host / in Cosmos with -v /:/mnt/host to enable folder creations, or create the bind folder yourself", err)
|
||||
return "", errors.New("Unable to create directory for bind mount in the host directory. Please mount the host / in Cosmos with -v /:/mnt/host to enable folder creations, or create the bind folder yourself")
|
||||
}
|
||||
newSource = "/mnt/host" + newSource
|
||||
}
|
||||
|
||||
utils.Log(fmt.Sprintf("Checking directory %s for bind mount", newSource))
|
||||
|
||||
if _, err := os.Stat(newSource); os.IsNotExist(err) {
|
||||
utils.Log(fmt.Sprintf("Not found. Creating directory %s for bind mount", newSource))
|
||||
|
||||
err := os.MkdirAll(newSource, 0755)
|
||||
if err != nil {
|
||||
utils.Error("EditContainer: Unable to create directory for bind mount", err)
|
||||
return "", errors.New("Unable to create directory for bind mount. Make sure parent directories exist, and that Cosmos has permissions to create directories in the host directory")
|
||||
}
|
||||
|
||||
if newConfig.Config.User != "" {
|
||||
// Change the ownership of the directory to the container.User
|
||||
userInfo, err := user.Lookup(newConfig.Config.User)
|
||||
if err != nil {
|
||||
utils.Error("EditContainer: Unable to lookup user", err)
|
||||
} else {
|
||||
uid, _ := strconv.Atoi(userInfo.Uid)
|
||||
gid, _ := strconv.Atoi(userInfo.Gid)
|
||||
err = os.Chown(newSource, uid, gid)
|
||||
if err != nil {
|
||||
utils.Error("EditContainer: Unable to change ownership of directory", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
utils.Log("EditContainer - Container updating. Retriveing currently running " + oldContainerID)
|
||||
|
||||
var err error
|
||||
|
|
|
@ -4,12 +4,13 @@ import (
|
|||
"net/http"
|
||||
"net/http/httputil"
|
||||
"net/url"
|
||||
"crypto/tls"
|
||||
spa "github.com/roberthodgen/spa-server"
|
||||
"github.com/azukaar/cosmos-server/src/utils"
|
||||
)
|
||||
|
||||
// NewProxy takes target host and creates a reverse proxy
|
||||
func NewProxy(targetHost string) (*httputil.ReverseProxy, error) {
|
||||
func NewProxy(targetHost string, AcceptInsecureHTTPSTarget bool) (*httputil.ReverseProxy, error) {
|
||||
url, err := url.Parse(targetHost)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -17,6 +18,12 @@ func NewProxy(targetHost string) (*httputil.ReverseProxy, error) {
|
|||
|
||||
proxy := httputil.NewSingleHostReverseProxy(url)
|
||||
|
||||
if AcceptInsecureHTTPSTarget && url.Scheme == "https" {
|
||||
proxy.Transport = &http.Transport{
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
||||
}
|
||||
}
|
||||
|
||||
proxy.ModifyResponse = func(resp *http.Response) error {
|
||||
utils.Debug("Response from backend: " + resp.Status)
|
||||
utils.Debug("URL was " + resp.Request.URL.String())
|
||||
|
@ -35,7 +42,7 @@ func RouteTo(route utils.ProxyRouteConfig) http.Handler {
|
|||
routeType := route.Mode
|
||||
|
||||
if(routeType == "SERVAPP" || routeType == "PROXY") {
|
||||
proxy, err := NewProxy(destination)
|
||||
proxy, err := NewProxy(destination, route.AcceptInsecureHTTPSTarget)
|
||||
if err != nil {
|
||||
utils.Error("Create Route", err)
|
||||
}
|
||||
|
|
|
@ -148,6 +148,7 @@ type ProxyRouteConfig struct {
|
|||
Mode ProxyMode
|
||||
BlockCommonBots bool
|
||||
BlockAPIAbuse bool
|
||||
AcceptInsecureHTTPSTarget bool
|
||||
}
|
||||
|
||||
type EmailConfig struct {
|
||||
|
|
|
@ -8,7 +8,6 @@ export default defineConfig({
|
|||
build: {
|
||||
outDir: '../static',
|
||||
},
|
||||
// base: '/ui',
|
||||
server: {
|
||||
proxy: {
|
||||
'/cosmos/api': {
|
||||
|
@ -16,6 +15,10 @@ export default defineConfig({
|
|||
secure: false,
|
||||
ws: true,
|
||||
}
|
||||
},
|
||||
|
||||
watch: {
|
||||
usePolling: true
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
Loading…
Reference in a new issue