[skip ci] pre-0.14

This commit is contained in:
Yann Stepienik
2024-01-21 14:56:17 +00:00
parent b0bc6b6126
commit e46dc10fcc
13 changed files with 368 additions and 501 deletions
+2
View File
@@ -3,6 +3,8 @@
- Improved network IP resolution for containers, including supporting any network mode
- Integrated MongoDB as container
- Removed all sort of container bootstrapping (much faster boot)
- Added image clean up
- Replaced network clean up by vanilla docker prune
## Version 0.13.2
- Fix display issue with fault network configurations
+2 -2
View File
@@ -178,9 +178,9 @@ function createService(serviceData, onProgress) {
const requestOptions = {
method: 'POST',
headers: {
'Content-Type': 'application/yaml'
'Content-Type': 'application/json'
},
body: serviceData
body: JSON.stringify(serviceData)
};
return fetch('/cosmos/api/docker-service', requestOptions)
+9 -1
View File
@@ -118,6 +118,7 @@ const ConfigManagement = () => {
Email_AllowInsecureTLS : config.EmailConfig.AllowInsecureTLS,
SkipPruneNetwork: config.DockerConfig.SkipPruneNetwork,
SkipPruneImages: config.DockerConfig.SkipPruneImages,
DefaultDataPath: config.DockerConfig.DefaultDataPath || "/usr",
Background: config && config.HomepageConfig && config.HomepageConfig.Background,
@@ -175,6 +176,7 @@ const ConfigManagement = () => {
DockerConfig: {
...config.DockerConfig,
SkipPruneNetwork: values.SkipPruneNetwork,
SkipPruneImages: values.SkipPruneImages,
DefaultDataPath: values.DefaultDataPath
},
HomepageConfig: {
@@ -537,11 +539,17 @@ const ConfigManagement = () => {
<MainCard title="Docker">
<Stack spacing={2}>
<CosmosCheckbox
label="Skip Prune Network"
label="Do not clean up Network"
name="SkipPruneNetwork"
formik={formik}
/>
<CosmosCheckbox
label="Do not clean up Images"
name="SkipPruneImages"
formik={formik}
/>
<CosmosInputText
label="Default data path for installs"
name="DefaultDataPath"
@@ -35,6 +35,7 @@ import cmp from 'semver-compare';
import { HostnameChecker, getHostnameFromName } from '../../../utils/routes';
import { CosmosContainerPicker } from '../../config/users/containerPicker';
import { randomString } from '../../../utils/indexs';
import { has } from 'lodash';
function checkIsOnline() {
API.isOnline().then((res) => {
@@ -176,196 +177,244 @@ const DockerComposeImport = ({ refresh, dockerComposeInit, installerInit, defaul
try {
doc = yaml.load(dockerCompose);
// if (typeof doc === 'object' && doc !== null && Object.keys(doc).length > 0 &&
// !doc.services && !doc.networks && !doc.volumes) {
// doc = {
// services: Object.assign({}, doc)
// }
// }
if (typeof doc === 'object' && doc !== null && Object.keys(doc).length > 0 &&
!doc.services && !doc.networks && !doc.volumes) {
doc = {
services: Object.assign({}, doc)
}
}
// // convert to the proper format
// if (doc.services) {
// Object.keys(doc.services).forEach((key) => {
// // convert 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] === '/' || volume[0] === '.') ? 'bind' : 'volume',
// };
// volumes.push(volumeObj);
// }
// });
// doc.services[key].volumes = volumes;
// }
// }
// convert to the proper format
if (doc.services) {
Object.keys(doc.services).forEach((key) => {
// convert 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] === '/' || volume[0] === '.') ? 'bind' : 'volume',
};
volumes.push(volumeObj);
}
});
doc.services[key].volumes = volumes;
}
}
// if(doc.services[key].volumes)
// Object.values(doc.services[key].volumes).forEach((volume) => {
// if (volume.source && volume.source[0] === '.') {
// let defaultPath = (config && config.DockerConfig && config.DockerConfig.DefaultDataPath) || "/usr"
// volume.source = defaultPath + volume.source.replace('.', '');
// }
// });
if(doc.services[key].volumes)
Object.values(doc.services[key].volumes).forEach((volume) => {
if (volume.source && volume.source[0] === '.') {
let defaultPath = (config && config.DockerConfig && config.DockerConfig.DefaultDataPath) || "/usr"
volume.source = defaultPath + volume.source.replace('.', '');
}
});
// // convert expose
// if (doc.services[key].expose) {
// doc.services[key].expose = doc.services[key].expose.map((port) => {
// return '' + port;
// })
// }
// convert expose
if (doc.services[key].expose) {
doc.services[key].expose = doc.services[key].expose.map((port) => {
return '' + port;
})
}
// //convert user
// if (doc.services[key].user) {
// doc.services[key].user = '' + doc.services[key].user;
// }
//convert 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)) {
// let labels = {};
// doc.services[key].labels.forEach((label) => {
// const [key, value] = label.split(/=(.*)/s);
// 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 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(/=(.*)/s);
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)) {
// let environment = [];
// Object.keys(doc.services[key].environment).forEach((keyenv) => {
// environment.push(keyenv + '=' + doc.services[key].environment[keyenv]);
// });
// doc.services[key].environment = environment;
// }
// }
// convert 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]);
});
doc.services[key].environment = environment;
}
}
// // convert network
// if (doc.services[key].networks) {
// if (Array.isArray(doc.services[key].networks)) {
// let networks = {};
// doc.services[key].networks.forEach((network) => {
// if (typeof network === 'object') {
// networks['' + network.name] = network;
// }
// else
// networks['' + network] = {};
// });
// doc.services[key].networks = networks;
// }
// }
// convert network
if (doc.services[key].networks) {
if (Array.isArray(doc.services[key].networks)) {
let networks = {};
doc.services[key].networks.forEach((network) => {
if (typeof network === 'object') {
networks['' + network.name] = network;
}
else
networks['' + network] = {};
});
doc.services[key].networks = networks;
}
}
// // convert devices
// if (doc.services[key].devices) {
// console.log(1)
// if (Array.isArray(doc.services[key].devices)) {
// console.log(2)
// let devices = [];
// doc.services[key].devices.forEach((device) => {
// if(device.indexOf(':') === -1) {
// devices.push(device + ':' + device);
// } else {
// devices.push(device);
// }
// });
// doc.services[key].devices = devices;
// }
// }
// convert devices
if (doc.services[key].devices) {
console.log(1)
if (Array.isArray(doc.services[key].devices)) {
console.log(2)
let devices = [];
doc.services[key].devices.forEach((device) => {
if(device.indexOf(':') === -1) {
devices.push(device + ':' + device);
} else {
devices.push(device);
}
});
doc.services[key].devices = devices;
}
}
// // convert command
// if (doc.services[key].command) {
// if (typeof doc.services[key].command !== 'string') {
// doc.services[key].command = doc.services[key].command.join(' ');
// }
// }
// convert command
if (doc.services[key].command) {
if (typeof doc.services[key].command !== 'string') {
doc.services[key].command = doc.services[key].command.join(' ');
}
}
// // ensure container_name
// if (!doc.services[key].container_name) {
// doc.services[key].container_name = key;
// }
// ensure container_name
if (!doc.services[key].container_name) {
doc.services[key].container_name = key;
}
// // convert healthcheck
// if (doc.services[key].healthcheck) {
// const toConvert = ["timeout", "interval", "start_period"];
// toConvert.forEach((valT) => {
// if(typeof doc.services[key].healthcheck[valT] === 'string') {
// let original = doc.services[key].healthcheck[valT];
// let value = parseInt(original);
// if (original.endsWith('m')) {
// value = value * 60;
// } else if (original.endsWith('h')) {
// value = value * 60 * 60;
// } else if (original.endsWith('d')) {
// value = value * 60 * 60 * 24;
// }
// doc.services[key].healthcheck[valT] = value;
// }
// });
// }
// });
// }
// convert healthcheck
if (doc.services[key].healthcheck) {
const toConvert = ["timeout", "interval", "start_period"];
toConvert.forEach((valT) => {
if(typeof doc.services[key].healthcheck[valT] === 'string') {
let original = doc.services[key].healthcheck[valT];
let value = parseInt(original);
if (original.endsWith('m')) {
value = value * 60;
} else if (original.endsWith('h')) {
value = value * 60 * 60;
} else if (original.endsWith('d')) {
value = value * 60 * 60 * 24;
}
doc.services[key].healthcheck[valT] = value;
}
});
}
});
}
// // 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;
// }
// }
// 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;
}
}
// // 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;
// }
// }
// 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;
}
}
// create default network
let hasDefaultNetwork = false;
if (doc.services) {
Object.keys(doc.services).forEach((key) => {
if(!doc.services[key].network_mode) {
doc.services[key].network_mode = 'cosmos-' + serviceName + '-default';
hasDefaultNetwork = true;
}
});
}
if(hasDefaultNetwork) {
if(!doc.networks) {
doc.networks = {}
}
doc.networks['cosmos-' + serviceName + '-default'] = {
Labels: {
'cosmos.stack': serviceName,
}
}
}
// stack up
if (doc.services && Object.keys(doc.services).length > 1) {
let hasMain = false;
Object.keys(doc.services).forEach((key) => {
if(!doc.services[key].labels) {
doc.services[key].labels = {};
}
if(!doc.services[key].labels['cosmos.stack'])
doc.services[key].labels['cosmos.stack'] = serviceName;
if(doc.services[key].labels['cosmos.stack.main'])
hasMain = true;
});
if(!hasMain) {
Object.keys(doc.services).forEach((key) => {
if(!hasMain) {
if(doc.services[key].labels['cosmos.stack'] == serviceName) {
doc.services[key].labels['cosmos.stack.main'] = true;
hasMain = true;
}
}
});
}
}
} catch (e) {
setYmlError(e.message);
@@ -21,7 +21,6 @@ import { Link } from 'react-router-dom';
import { smartDockerLogConcat, tryParseProgressLog } from '../../../utils/docker';
import { LoadingButton } from '@mui/lab';
import LogLine from '../../../components/logLine';
import yaml from 'js-yaml';
const preStyle = {
backgroundColor: '#000',
@@ -85,7 +84,7 @@ const NewDockerService = ({service, refresh}) => {
setLog([
'Creating Service... ',
])
API.docker.createService(yaml.dump(service), (newlog) => {
API.docker.createService(service, (newlog) => {
setLog((old) => smartDockerLogConcat(old, newlog));
preRef.current.scrollTop = preRef.current.scrollHeight;
if (newlog.includes('[OPERATION SUCCEEDED]')) {
@@ -119,7 +118,7 @@ const NewDockerService = ({service, refresh}) => {
<pre style={preStyle} ref={preRef}>
{!log.length && `
# You are about to create the following service(s):
${yaml.dump(service)}`
${JSON.stringify(service, false ,2)}`
}
{log.map((l) => {
return <LogLine message={tryParseProgressLog(l)} docker isMobile={!screenMin} />
@@ -85,7 +85,12 @@ const VolumeContainerSetup = ({ noCard, containerInfo, frozenVolumes = [], refre
if(newContainer) return;
setSubmitting(true);
const realvalues = {
Volumes: values.volumes
Volumes: values.volumes.map((volume) => ({
Type: volume.Type,
Source: volume.Source,
Target: volume.Target,
ReadOnly: !volume.RW
}))
};
return API.docker.updateContainer(containerInfo.Name.replace('/', ''), realvalues)
.then((res) => {
+54 -53
View File
@@ -136,9 +136,9 @@ const ServApps = ({stack}) => {
const servAppsStacked = servApps && servApps.reduce((acc, app) => {
// if has label cosmos-stack, add to stack
if(!stack && (app.Labels['cosmos-stack'] || app.Labels['com.docker.compose.project'])) {
let stackName = app.Labels['cosmos-stack'] || app.Labels['com.docker.compose.project'];
let stackMain = app.Labels['cosmos-stack-main'] || (app.Labels['com.docker.compose.container-number'] == '1' && app.Names[0].replace('/', ''));
if(!stack && (app.Labels['cosmos-stack'] || app.Labels['cosmos.stack'] || app.Labels['com.docker.compose.project'])) {
let stackName = app.Labels['cosmos-stack'] || app.Labels['cosmos.stack'] || app.Labels['com.docker.compose.project'];
let stackMain = app.Labels['cosmos-stack-main'] || app.Labels['cosmos.stack.main'] || (app.Labels['com.docker.compose.container-number'] == '1' && app.Names[0].replace('/', ''));
if(!acc[stackName]) {
acc[stackName] = {
@@ -190,7 +190,7 @@ const ServApps = ({stack}) => {
if(stackMain == app.Names[0].replace('/', '') || !acc[stackName].app) {
acc[stackName].app = app;
}
} else if (!stack || (stack && (app.Labels['cosmos-stack'] === stack || app.Labels['com.docker.compose.project'] === stack))){
} else if (!stack || (stack && (app.Labels['cosmos-stack'] === stack || app.Labels['cosmos.stack'] === stack || app.Labels['com.docker.compose.project'] === stack))){
// else add to default stack
acc[app.Names[0]] = {
type: 'app',
@@ -370,55 +370,6 @@ const ServApps = ({stack}) => {
})}
</Stack>
</Stack>
{app.isUpdating ? <div>
<CircularProgress color="inherit" />
</div>
:
<Stack margin={1} direction="column" spacing={1} alignItems="flex-start">
<Typography variant="h6" color="text.secondary">
Settings {app.type == "app" && (app.state !== 'running' ? '(Start container to edit)' : '')}
</Typography>
<Stack style={{ fontSize: '80%' }} direction={"row"} alignItems="center">
<Checkbox
checked={app.labels['cosmos-force-network-secured'] === 'true'}
disabled={app.type == "stack" || app.state !== 'running'}
onChange={(e) => {
const name = app.name.replace('/', '');
setIsUpdatingId(name, true);
API.docker.secure(name, e.target.checked).then(() => {
setTimeout(() => {
setIsUpdatingId(name, false);
refreshServApps();
}, 3000);
}).catch(() => {
setIsUpdatingId(name, false);
refreshServApps();
})
}}
/> Isolate Container Network <ContainerNetworkWarning container={app.app} />
</Stack>
<Stack style={{ fontSize: '80%' }} direction={"row"} alignItems="center">
<Checkbox
checked={app.labels['cosmos-auto-update'] === 'true' ||
(selfName && app.name.replace('/', '') == selfName && config.AutoUpdate)}
disabled={app.type == "stack" || app.state !== 'running'}
onChange={(e) => {
const name = app.name.replace('/', '');
setIsUpdatingId(name, true);
API.docker.autoUpdate(name, e.target.checked).then(() => {
setTimeout(() => {
setIsUpdatingId(name, false);
refreshServApps();
}, 3000);
}).catch(() => {
setIsUpdatingId(name, false);
refreshServApps();
})
}}
/> Auto Update Container
</Stack>
</Stack>
}
<Stack margin={1} direction="column" spacing={1} alignItems="flex-start">
<Typography variant="h6" color="text.secondary">
URLs
@@ -443,6 +394,55 @@ const ServApps = ({stack}) => {
{/* } */}
</Stack>
</Stack>
{app.isUpdating ? <div>
<CircularProgress color="inherit" />
</div>
:
<Stack margin={1} direction="column" spacing={1} alignItems="flex-start">
{/* <Typography variant="h6" color="text.secondary">
Settings {app.type == "app" && (app.state !== 'running' ? '(Start container to edit)' : '')}
</Typography>
<Stack style={{ fontSize: '80%' }} direction={"row"} alignItems="center">
<Checkbox
checked={app.labels['cosmos-force-network-secured'] === 'true'}
disabled={app.type == "stack" || app.state !== 'running'}
onChange={(e) => {
const name = app.name.replace('/', '');
setIsUpdatingId(name, true);
API.docker.secure(name, e.target.checked).then(() => {
setTimeout(() => {
setIsUpdatingId(name, false);
refreshServApps();
}, 3000);
}).catch(() => {
setIsUpdatingId(name, false);
refreshServApps();
})
}}
/> Isolate Container Network <ContainerNetworkWarning container={app.app} />
</Stack> */}
<Stack style={{ fontSize: '80%' }} direction={"row"} alignItems="center">
<Checkbox
checked={app.labels['cosmos-auto-update'] === 'true' ||
(selfName && app.name.replace('/', '') == selfName && config.AutoUpdate)}
disabled={app.type == "stack" || app.state !== 'running'}
onChange={(e) => {
const name = app.name.replace('/', '');
setIsUpdatingId(name, true);
API.docker.autoUpdate(name, e.target.checked).then(() => {
setTimeout(() => {
setIsUpdatingId(name, false);
refreshServApps();
}, 3000);
}).catch(() => {
setIsUpdatingId(name, false);
refreshServApps();
})
}}
/> Auto Update Container
</Stack>
</Stack>
}
<div>
<MiniPlotComponent agglo metrics={[
"cosmos.system.docker.cpu." + app.name.replace('/', ''),
@@ -452,6 +452,7 @@ const ServApps = ({stack}) => {
["cosmos.system.docker.ram." + app.name.replace('/', '')]: "RAM"
}}/>
</div>
<div>
<Link to={app.type === 'stack' ?
`/cosmos-ui/servapps/stack/${app.name}` :
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "cosmos-server",
"version": "0.13.2",
"version": "0.14.0-unstable1",
"description": "",
"main": "test-server.js",
"bugs": {
+1
View File
@@ -11,6 +11,7 @@
<a href="https://github.com/BlackrazorNZ"><img src="https://avatars.githubusercontent.com/BlackrazorNZ" style="border-radius:48px" width="48" height="48" alt="null" title="null" /></a>
<a href="https://github.com/DGAzr"><img src="https://avatars.githubusercontent.com/DGAzr" style="border-radius:48px" width="48" height="48" alt="null" title="null" /></a>
<a href="https://github.com/eldergod1800"><img src="https://avatars.githubusercontent.com/eldergod1800" style="border-radius:48px" width="48" height="48" alt="null" title="null" /></a>
<a href="https://github.com/MELVARDEV"><img src="https://avatars.githubusercontent.com/MELVARDEV" style="border-radius:48px" width="48" height="48" alt="MELVAR" title="MELVAR" /></a>
<a href="https://github.com/Quinnus"><img src="https://avatars.githubusercontent.com/Quinnus" style="border-radius:48px" width="48" height="48" alt="null" title="null" /></a>
</p><!-- /sponsors -->
+49 -121
View File
@@ -1,7 +1,7 @@
package docker
import (
// "encoding/json"
"encoding/json"
"fmt"
"net/http"
"strings"
@@ -12,7 +12,6 @@ import (
"io/ioutil"
"os/user"
"errors"
// "gopkg.in/yaml.v2"
"github.com/docker/go-connections/nat"
"github.com/docker/docker/api/types/mount"
"github.com/docker/docker/api/types/network"
@@ -230,76 +229,23 @@ func CreateServiceRoute(w http.ResponseWriter, req *http.Request) {
return
}
// decoder := yaml.NewDecoder(req.Body)
// var serviceRequest DockerServiceCreateRequest
// err := decoder.Decode(&serviceRequest)
// if err != nil {
// utils.Error("CreateService - decode - ", err)
// fmt.Fprintf(w, "[OPERATION FAILED] Bad request: "+err.Error(), http.StatusBadRequest, "DS003")
// flusher.Flush()
// utils.HTTPError(w, "Bad request: " + err.Error(), http.StatusBadRequest, "DS003")
// return
// }
decoder := json.NewDecoder(req.Body)
var serviceRequest DockerServiceCreateRequest
err := decoder.Decode(&serviceRequest)
if err != nil {
utils.Error("CreateService - decode - ", err)
fmt.Fprintf(w, "[OPERATION FAILED] Bad request: "+err.Error(), http.StatusBadRequest, "DS003")
flusher.Flush()
utils.HTTPError(w, "Bad request: " + err.Error(), http.StatusBadRequest, "DS003")
return
}
/*CreateService(serviceRequest,
CreateService(serviceRequest,
func (msg string) {
fmt.Fprintf(w, msg)
flusher.Flush()
},
)*/
ServiceName := "test-wesh"
filePath := utils.CONFIGFOLDER + "compose/" + ServiceName
// Create the folder if does not exist, and output docker-compose.yml
if _, err := os.Stat(utils.CONFIGFOLDER + "compose/"); os.IsNotExist(err) {
os.MkdirAll(utils.CONFIGFOLDER + "compose/", 0750)
}
if _, err := os.Stat(filePath); os.IsNotExist(err) {
os.MkdirAll(filePath, 0750)
}
// create or truncate the file
file, err := os.Create(filePath + "/docker-compose.yml")
if err != nil {
utils.Error("CreateService - create - ", err)
fmt.Fprintf(w, "[OPERATION FAILED] Internal server error: "+err.Error(), http.StatusInternalServerError, "DS004")
flusher.Flush()
return
}
defer file.Close()
// write to file
ymlBody := req.Body
writer := bufio.NewWriter(file)
_, err = writer.ReadFrom(ymlBody)
if err != nil {
utils.Error("CreateService - write - ", err)
fmt.Fprintf(w, "[OPERATION FAILED] Internal server error: "+err.Error(), http.StatusInternalServerError, "DS005")
flusher.Flush()
return
}
writer.Flush()
// Compose up
err = ComposeUp(filePath, func(message string, outputType int) {
fmt.Fprintf(w, "%s\n", message)
flusher.Flush()
})
if err != nil {
utils.Error("CreateService - composeup - ", err)
fmt.Fprintf(w, "[OPERATION FAILED] Internal server error: "+err.Error(), http.StatusInternalServerError, "DS006")
flusher.Flush()
return
}
// Write a response to the client
fmt.Fprintf(w, "[OPERATION SUCCESSFUL] Service created successfully", http.StatusOK, "DS007")
flusher.Flush()
)
} else {
utils.Error("CreateService: Method not allowed" + req.Method, nil)
utils.HTTPError(w, "Method not allowed", http.StatusMethodNotAllowed, "HTTP001")
@@ -340,21 +286,6 @@ func CreateService(serviceRequest DockerServiceCreateRequest, OnLog func(string)
var rollbackActions []DockerServiceCreateRollback
var err error
serviceName := ""
for serviceName = range serviceRequest.Services {
break
}
// check if serviceRequest.Networks contains service-default, if not, create it
if _, ok := serviceRequest.Networks[serviceName + "-default"]; !ok {
serviceRequest.Networks[serviceName + "-default"] = ContainerCreateRequestNetwork{
Name: serviceName + "-default",
Driver: "bridge",
Attachable: true,
Internal: false,
}
}
// Create networks
for networkToCreateName, networkToCreate := range serviceRequest.Networks {
utils.Log(fmt.Sprintf("Creating network %s...", networkToCreateName))
@@ -365,7 +296,7 @@ func CreateService(serviceRequest DockerServiceCreateRequest, OnLog func(string)
if err == nil {
if networkToCreate.Driver == "" {
networkToCreate.Driver = serviceName + "-default"
networkToCreate.Driver = "bridge"
}
if (exNetworkDef.Driver != networkToCreate.Driver) {
@@ -491,51 +422,50 @@ func CreateService(serviceRequest DockerServiceCreateRequest, OnLog func(string)
OnLog(fmt.Sprintf("Checking service %s...\n", serviceName))
// If container request a Cosmos network, create and attach it
/*if (container.Labels["cosmos-force-network-secured"] == "true" || strings.ToLower(container.Labels["cosmos-network-name"]) == "auto") &&
container.Labels["cosmos-network-name"] == "" {
utils.Log(fmt.Sprintf("Forcing secure %s...", serviceName))
OnLog(fmt.Sprintf("Forcing secure %s...\n", serviceName))
// if (container.Labels["cosmos-force-network-secured"] == "true" || strings.ToLower(container.Labels["cosmos-network-name"]) == "auto") &&
// container.Labels["cosmos-network-name"] == "" {
// utils.Log(fmt.Sprintf("Forcing secure %s...", serviceName))
// OnLog(fmt.Sprintf("Forcing secure %s...\n", serviceName))
newNetwork, errNC := CreateCosmosNetwork(serviceName)
if errNC != nil {
utils.Error("CreateService: Network", err)
OnLog(utils.DoErr("Network %s cant be created\n", newNetwork))
Rollback(rollbackActions, OnLog)
return err
}
// newNetwork, errNC := CreateCosmosNetwork(serviceName)
// if errNC != nil {
// utils.Error("CreateService: Network", err)
// OnLog(utils.DoErr("Network %s cant be created\n", newNetwork))
// Rollback(rollbackActions, OnLog)
// return err
// }
container.Labels["cosmos-network-name"] = newNetwork
// container.Labels["cosmos-network-name"] = newNetwork
AttachNetworkToCosmos(newNetwork)
// AttachNetworkToCosmos(newNetwork)
if container.Networks == nil {
container.Networks = make(map[string]ContainerCreateRequestServiceNetwork)
}
// if container.Networks == nil {
// container.Networks = make(map[string]ContainerCreateRequestServiceNetwork)
// }
container.Networks[newNetwork] = ContainerCreateRequestServiceNetwork{}
// container.Networks[newNetwork] = ContainerCreateRequestServiceNetwork{}
rollbackActions = append(rollbackActions, DockerServiceCreateRollback{
Action: "remove",
Type: "network",
Name: newNetwork,
})
// rollbackActions = append(rollbackActions, DockerServiceCreateRollback{
// Action: "remove",
// Type: "network",
// Name: newNetwork,
// })
utils.Log(fmt.Sprintf("Created secure network %s", newNetwork))
OnLog(fmt.Sprintf("Created secure network %s\n", newNetwork))
} else if container.Labels["cosmos-network-name"] != "" {
// Container has a declared a Cosmos network, check if it exists and connect to it
utils.Log(fmt.Sprintf("Checking declared network %s...", container.Labels["cosmos-network-name"]))
OnLog(fmt.Sprintf("Checking declared network %s...\n", container.Labels["cosmos-network-name"]))
// utils.Log(fmt.Sprintf("Created secure network %s", newNetwork))
// OnLog(fmt.Sprintf("Created secure network %s\n", newNetwork))
// } else if container.Labels["cosmos-network-name"] != "" {
// // Container has a declared a Cosmos network, check if it exists and connect to it
// utils.Log(fmt.Sprintf("Checking declared network %s...", container.Labels["cosmos-network-name"]))
// OnLog(fmt.Sprintf("Checking declared network %s...\n", container.Labels["cosmos-network-name"]))
_, err := DockerClient.NetworkInspect(DockerContext, container.Labels["cosmos-network-name"], doctype.NetworkInspectOptions{})
if err == nil {
utils.Log(fmt.Sprintf("Connecting to declared network %s...", container.Labels["cosmos-network-name"]))
OnLog(fmt.Sprintf("Connecting to declared network %s...\n", container.Labels["cosmos-network-name"]))
// _, err := DockerClient.NetworkInspect(DockerContext, container.Labels["cosmos-network-name"], doctype.NetworkInspectOptions{})
// if err == nil {
// utils.Log(fmt.Sprintf("Connecting to declared network %s...", container.Labels["cosmos-network-name"]))
// OnLog(fmt.Sprintf("Connecting to declared network %s...\n", container.Labels["cosmos-network-name"]))
AttachNetworkToCosmos(container.Labels["cosmos-network-name"])
}
}*/
// AttachNetworkToCosmos(container.Labels["cosmos-network-name"])
// }
// }
utils.Log(fmt.Sprintf("Creating container %s...", container.Name))
OnLog(fmt.Sprintf("Creating container %s...\n", container.Name))
@@ -889,8 +819,6 @@ func CreateService(serviceRequest DockerServiceCreateRequest, OnLog func(string)
// connect to networks
for netName, netConfig := range container.Networks {
utils.Log("CreateService: Connecting to network: " + netName)
//TODO: THIS IS WRONG https://pkg.go.dev/github.com/docker/docker@v24.0.7+incompatible/api/types/network#EndpointSettings
err = DockerClient.NetworkConnect(DockerContext, netName, container.Name, &network.EndpointSettings{
Aliases: netConfig.Aliases,
IPAddress: netConfig.IPV4Address,
-58
View File
@@ -1,58 +0,0 @@
package docker
import (
"bufio"
"io"
"os/exec"
"github.com/azukaar/cosmos-server/src/utils"
)
const (
Stdout = iota
Stderr
)
// readOutput reads from the given reader and uses the callback function to handle the output.
func readOutput(r io.Reader, callback func(message string, outputType int), outputType int) {
scanner := bufio.NewScanner(r)
for scanner.Scan() {
callback(scanner.Text(), outputType)
}
}
// ExecCommand runs the specified command and uses a callback function to handle the output.
func ExecCommand(callback func(message string, outputType int), args ...string) error {
cmd := exec.Command(args[0], args[1:]...)
utils.Debug("Running command: " + cmd.String())
callback("Running command: " + cmd.String(), Stdout)
// Create pipes for stdout and stderr
stdoutPipe, err := cmd.StdoutPipe()
if err != nil {
return err
}
stderrPipe, err := cmd.StderrPipe()
if err != nil {
return err
}
// Start the command
if err := cmd.Start(); err != nil {
return err
}
// Read from stdout and stderr in separate goroutines
go readOutput(stdoutPipe, callback, Stdout)
go readOutput(stderrPipe, callback, Stderr)
// Wait for the command to finish
return cmd.Wait()
}
func ComposeUp(filePath string, callback func(message string, outputType int)) error {
return ExecCommand(callback, "docker", "compose", "--project-directory", filePath, "up", "--remove-orphans", "-d")
}
+15 -84
View File
@@ -9,7 +9,7 @@ import (
"fmt"
"github.com/azukaar/cosmos-server/src/utils"
"github.com/docker/docker/api/types/filters"
"github.com/docker/docker/api/types"
network "github.com/docker/docker/api/types/network"
natting "github.com/docker/go-connections/nat"
@@ -393,95 +393,26 @@ var DebouncedNetworkCleanUp = _debounceNetworkCleanUp()
func NetworkCleanUp() {
config := utils.GetMainConfig()
if(config.DockerConfig.SkipPruneNetwork) {
return
}
DockerNetworkLock <- true
defer func() { <-DockerNetworkLock }()
defer func() { <-DockerNetworkLock }()
utils.Log("Cleaning up orphan networks...")
pruneFilters := filters.NewArgs()
// list every network
networks, err := DockerClient.NetworkList(DockerContext, types.NetworkListOptions{})
if err != nil {
utils.Error("NetworkCleanUpList", err)
return
if(!config.DockerConfig.SkipPruneNetwork) {
report, err := DockerClient.NetworksPrune(DockerContext, pruneFilters)
if err != nil {
utils.Error("[DOCKER] Error pruning networks", err)
}
utils.Log("Pruned networks: " + fmt.Sprintf("%v", report.NetworksDeleted))
}
// check if network is empty or has only self as container
for _, networkHollow := range networks {
utils.Debug("Checking network: " + networkHollow.Name)
if(networkHollow.Name == "bridge" || networkHollow.Name == "host" || networkHollow.Name == "none") {
continue
}
// inspect network because the Docker API is a complete mess :)
network, err := DockerClient.NetworkInspect(DockerContext, networkHollow.ID, types.NetworkInspectOptions{})
if(!config.DockerConfig.SkipPruneImages) {
report, err := DockerClient.ImagesPrune(DockerContext, pruneFilters)
if err != nil {
utils.Error("NetworkCleanUpInspect", err)
continue
}
if(len(network.Containers) > 1) {
continue
}
utils.Debug("Ready to Check network: " + network.Name)
if(len(network.Containers) == 0) {
utils.Log("Removing orphan network: " + network.Name)
err := DockerClient.NetworkRemove(DockerContext, network.ID)
if err != nil {
utils.Error("DockerNetworkCleanupRemove", err)
}
continue
}
self := os.Getenv("HOSTNAME")
if self == "" {
utils.Warn("Skipping zombie network cleanup because not a docker cosmos container")
continue
}
utils.Debug("Checking self name: " + self)
utils.Debug("Checking non-empty network: " + network.Name)
containsCosmos := false
for _, container := range network.Containers {
utils.Debug("Checking name: " + container.Name)
if(container.Name == self) {
containsCosmos = true
}
utils.Error("[DOCKER] Error pruning images", err)
}
if(containsCosmos) {
// docker inspect self
selfContainer, err := DockerClient.ContainerInspect(DockerContext, self)
if err != nil {
utils.Error("NetworkCleanUpInspectSelf", err)
continue
}
// check if self is network_mode to this network
if(string(selfContainer.HostConfig.NetworkMode) == network.Name) {
utils.Warn("Skipping network cleanup because self is network_mode to this network")
continue
}
utils.Log("Disconnecting and removing zombie network: " + network.Name)
err = DockerClient.NetworkDisconnect(DockerContext, network.ID, self, true)
if err != nil {
utils.Error("DockerNetworkCleanupDisconnect", err)
continue
}
err = DockerClient.NetworkRemove(DockerContext, network.ID)
if err != nil {
utils.Error("DockerNetworkCleanupRemove", err)
}
}
utils.Log("Pruned images: " + fmt.Sprintf("%v", report.ImagesDeleted))
}
}
+1
View File
@@ -147,6 +147,7 @@ type SmartShieldPolicy struct {
type DockerConfig struct {
SkipPruneNetwork bool
SkipPruneImages bool
DefaultDataPath string
}