[release] version 0.5.5
This commit is contained in:
parent
c8731e2fa7
commit
a6098f0507
|
@ -42,7 +42,55 @@ let isOnline = () => {
|
|||
});
|
||||
}
|
||||
|
||||
let newInstall = (req) => {
|
||||
let newInstall = (req, onProgress) => {
|
||||
if(req.step == '2') {
|
||||
const requestOptions = {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify(req)
|
||||
};
|
||||
|
||||
return fetch('/cosmos/api/newInstall', requestOptions)
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
throw new Error(response.statusText);
|
||||
}
|
||||
|
||||
// The response body is a ReadableStream. This code reads the stream and passes chunks to the callback.
|
||||
const reader = response.body.getReader();
|
||||
|
||||
// Read the stream and pass chunks to the callback as they arrive
|
||||
return new ReadableStream({
|
||||
start(controller) {
|
||||
function read() {
|
||||
return reader.read().then(({ done, value }) => {
|
||||
if (done) {
|
||||
controller.close();
|
||||
return;
|
||||
}
|
||||
// Decode the UTF-8 text
|
||||
let text = new TextDecoder().decode(value);
|
||||
// Split by lines in case there are multiple lines in one chunk
|
||||
let lines = text.split('\n');
|
||||
for (let line of lines) {
|
||||
if (line) {
|
||||
// Call the progress callback
|
||||
onProgress(line);
|
||||
}
|
||||
}
|
||||
controller.enqueue(value);
|
||||
return read();
|
||||
});
|
||||
}
|
||||
return read();
|
||||
}
|
||||
});
|
||||
}).catch((e) => {
|
||||
alert(e);
|
||||
});
|
||||
} else {
|
||||
return wrap(fetch('/cosmos/api/newInstall', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
|
@ -50,6 +98,7 @@ let newInstall = (req) => {
|
|||
},
|
||||
body: JSON.stringify(req)
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
const isDemo = import.meta.env.MODE === 'demo';
|
||||
|
|
9
client/src/components/containers.jsx
Normal file
9
client/src/components/containers.jsx
Normal file
|
@ -0,0 +1,9 @@
|
|||
import { WarningFilled } from "@ant-design/icons";
|
||||
import { Tooltip } from "@mui/material";
|
||||
|
||||
export const ContainerNetworkWarning = ({container}) => (
|
||||
container.HostConfig.NetworkMode != "bridge" && container.HostConfig.NetworkMode != "default" &&
|
||||
<Tooltip title={`This container is using an incompatible network mode (${container.HostConfig.NetworkMode.slice(0, 16)}). If you want Cosmos to proxy to this container, enabling this option will change the network mode to bridge for you. Otherwise, you dont need to do anything, as the container is already isolated. Note that changing to bridge might break connectivity to other containers. To fix it, please use a private network and static ips instead.`}>
|
||||
<WarningFilled style={{color: 'red', fontSize: '18px', paddingLeft: '10px', paddingRight: '10px'}} />
|
||||
</Tooltip>
|
||||
);
|
|
@ -14,6 +14,7 @@ import { useEffect, useState } from 'react';
|
|||
|
||||
import * as API from '../../api';
|
||||
import { Formik } from 'formik';
|
||||
import LogsInModal from '../../components/logsInModal';
|
||||
import { CosmosCheckbox, CosmosInputPassword, CosmosInputText, CosmosSelect } from '../config/users/formShortcuts';
|
||||
import AnimateButton from '../../components/@extended/AnimateButton';
|
||||
import { Box } from '@mui/system';
|
||||
|
@ -25,6 +26,7 @@ const NewInstall = () => {
|
|||
const [counter, setCounter] = useState(0);
|
||||
let [hostname, setHostname] = useState('');
|
||||
const [databaseEnable, setDatabaseEnable] = useState(true);
|
||||
const [pullRequest, setPullRequest] = useState(null);
|
||||
|
||||
const refreshStatus = async () => {
|
||||
try {
|
||||
|
@ -118,27 +120,29 @@ const NewInstall = () => {
|
|||
validate={(values) => {
|
||||
}}
|
||||
onSubmit={async (values, { setErrors, setStatus, setSubmitting }) => {
|
||||
try {
|
||||
setSubmitting(true);
|
||||
const res = await API.newInstall({
|
||||
|
||||
setPullRequest(() => ((cb) => {
|
||||
API.newInstall({
|
||||
step: "2",
|
||||
MongoDBMode: values.DBMode,
|
||||
MongoDB: values.MongoDB,
|
||||
});
|
||||
if(res.status == "OK") {
|
||||
if(values.DBMode === "DisableUserManagement") {
|
||||
setDatabaseEnable(false);
|
||||
}
|
||||
setStatus({ success: true });
|
||||
}
|
||||
} catch (error) {
|
||||
setStatus({ success: false });
|
||||
setErrors({ submit: error.message });
|
||||
setSubmitting(false);
|
||||
}
|
||||
}, cb)
|
||||
}));
|
||||
}}>
|
||||
{(formik) => (
|
||||
<form noValidate onSubmit={formik.handleSubmit}>
|
||||
<LogsInModal
|
||||
request={pullRequest}
|
||||
title="Installing Database..."
|
||||
OnSuccess={() => {
|
||||
if(formik.values.DBMode === "DisableUserManagement") {
|
||||
setDatabaseEnable(false);
|
||||
}
|
||||
formik.setStatus({ success: true });
|
||||
formik.setSubmitting(false);
|
||||
}}
|
||||
/>
|
||||
<Stack item xs={12} spacing={2}>
|
||||
<CosmosSelect
|
||||
name="DBMode"
|
||||
|
|
|
@ -16,7 +16,6 @@ const GetActions = ({
|
|||
const isMiniMobile = useMediaQuery((theme) => theme.breakpoints.down('xsm'));
|
||||
const [pullRequest, setPullRequest] = React.useState(null);
|
||||
const [isUpdating, setIsUpdating] = React.useState(false);
|
||||
console.log(isMiniMobile)
|
||||
|
||||
const doTo = (action) => {
|
||||
setIsUpdating(true);
|
||||
|
|
|
@ -116,6 +116,30 @@ const DockerComposeImport = ({refresh}) => {
|
|||
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('=');
|
||||
labels[''+key] = ''+value;
|
||||
});
|
||||
doc.services[key].labels = labels;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// convert network
|
||||
if(doc.services[key].networks) {
|
||||
if(Array.isArray(doc.services[key].networks)) {
|
||||
let networks = {};
|
||||
doc.services[key].networks.forEach((network) => {
|
||||
networks[''+network] = {};
|
||||
});
|
||||
doc.services[key].networks = networks;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -67,6 +67,7 @@ const NetworkContainerSetup = ({ config, containerInfo, refresh, newContainer, O
|
|||
<div style={{ maxWidth: '1000px', width: '100%', margin: '', position: 'relative' }}>
|
||||
<Formik
|
||||
initialValues={{
|
||||
networkMode: containerInfo.HostConfig.NetworkMode,
|
||||
ports: Object.keys(containerInfo.NetworkSettings.Ports).map((port) => {
|
||||
return {
|
||||
port: port.split('/')[0],
|
||||
|
@ -237,6 +238,14 @@ const NetworkContainerSetup = ({ config, containerInfo, refresh, newContainer, O
|
|||
</MainCard>
|
||||
<MainCard title={'Networks'}>
|
||||
<Stack spacing={2}>
|
||||
|
||||
<CosmosInputText
|
||||
label="Network Mode"
|
||||
name="networkMode"
|
||||
placeholder={'default'}
|
||||
formik={formik}
|
||||
/>
|
||||
|
||||
{networks && <Stack spacing={2}>
|
||||
{Object.keys(containerInfo.NetworkSettings.Networks).map((networkName) => {
|
||||
const network = networks.find((n) => n.Name === networkName);
|
||||
|
|
|
@ -99,7 +99,7 @@ const NewDockerServiceForm = () => {
|
|||
variant="contained"
|
||||
fullWidth
|
||||
endIcon={<ArrowRightOutlined />}
|
||||
disabled={currentTab === 4}
|
||||
disabled={(currentTab === 4) || containerInfo.Name === '' || containerInfo.Config.Image === ''}
|
||||
onClick={() => {
|
||||
setCurrentTab(currentTab + 1);
|
||||
setMaxTab(Math.max(currentTab + 1, maxTab));
|
||||
|
|
|
@ -108,8 +108,8 @@ const ContainerOverview = ({ containerInfo, config, refresh }) => {
|
|||
)}
|
||||
<strong><ContainerOutlined /> Image</strong>
|
||||
<div style={info}>{Image}</div>
|
||||
<strong><DesktopOutlined /> Name</strong>
|
||||
<div style={info}>{Name}</div>
|
||||
<strong><DesktopOutlined /> ID</strong>
|
||||
<div style={info}>{containerInfo.Id}</div>
|
||||
<strong><InfoCircleOutlined /> IP Address</strong>
|
||||
<div style={info}>{IPAddress}</div>
|
||||
<strong>
|
||||
|
|
|
@ -61,6 +61,9 @@ const DockerContainerSetup = ({config, containerInfo, OnChange, refresh, newCont
|
|||
if (!values.image) {
|
||||
errors.image = 'Required';
|
||||
}
|
||||
if (!values.name && newContainer) {
|
||||
errors.name = 'Required';
|
||||
}
|
||||
// env keys and labels key mustbe unique
|
||||
const envKeys = values.envVars.map((envVar) => envVar.key);
|
||||
const labelKeys = values.labels.map((label) => label.key);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// material-ui
|
||||
import { AppstoreAddOutlined, CloseSquareOutlined, DeleteOutlined, PauseCircleOutlined, PlaySquareOutlined, PlusCircleOutlined, ReloadOutlined, RollbackOutlined, SearchOutlined, SettingOutlined, StopOutlined, UpCircleOutlined, UpSquareFilled } from '@ant-design/icons';
|
||||
import { AlertFilled, AppstoreAddOutlined, CloseSquareOutlined, DeleteOutlined, PauseCircleOutlined, PlaySquareOutlined, PlusCircleOutlined, ReloadOutlined, RollbackOutlined, SearchOutlined, SettingOutlined, StopOutlined, UpCircleOutlined, UpSquareFilled, WarningFilled } from '@ant-design/icons';
|
||||
import { Alert, Badge, Button, Card, Checkbox, Chip, CircularProgress, Dialog, DialogActions, DialogContent, DialogContentText, DialogTitle, Divider, IconButton, Input, InputAdornment, TextField, Tooltip, Typography } from '@mui/material';
|
||||
import Grid2 from '@mui/material/Unstable_Grid2/Grid2';
|
||||
import { Stack } from '@mui/system';
|
||||
|
@ -18,6 +18,7 @@ import ExposeModal from './exposeModal';
|
|||
import GetActions from './actionBar';
|
||||
import ResponsiveButton from '../../components/responseiveButton';
|
||||
import DockerComposeImport from './containers/docker-compose';
|
||||
import { ContainerNetworkWarning } from '../../components/containers';
|
||||
|
||||
const Item = styled(Paper)(({ theme }) => ({
|
||||
backgroundColor: theme.palette.mode === 'dark' ? '#1A2027' : '#fff',
|
||||
|
@ -251,7 +252,7 @@ const ServeApps = () => {
|
|||
refreshServeApps();
|
||||
})
|
||||
}}
|
||||
/> Force Secure Network
|
||||
/> Force Secure Network <ContainerNetworkWarning container={app} />
|
||||
</Stack>
|
||||
<Stack style={{ fontSize: '80%' }} direction={"row"} alignItems="center">
|
||||
<Checkbox
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "cosmos-server",
|
||||
"version": "0.5.4",
|
||||
"version": "0.5.5",
|
||||
"description": "",
|
||||
"main": "test-server.js",
|
||||
"bugs": {
|
||||
|
|
|
@ -39,7 +39,7 @@ func AutoUpdateContainerRoute(w http.ResponseWriter, req *http.Request) {
|
|||
|
||||
utils.Log("API: Set Auto Update "+status+" : " + containerName)
|
||||
|
||||
_, errEdit := EditContainer(container.ID, container)
|
||||
_, errEdit := EditContainer(container.ID, container, false)
|
||||
if errEdit != nil {
|
||||
utils.Error("AutoUpdateContainer Edit", errEdit)
|
||||
utils.HTTPError(w, "Internal server error: " + errEdit.Error(), http.StatusInternalServerError, "DS003")
|
||||
|
|
|
@ -167,7 +167,6 @@ func CreateServiceRoute(w http.ResponseWriter, req *http.Request) {
|
|||
}
|
||||
|
||||
if req.Method == "POST" {
|
||||
// Enable streaming of response by setting appropriate headers
|
||||
w.Header().Set("X-Content-Type-Options", "nosniff")
|
||||
w.Header().Set("Transfer-Encoding", "chunked")
|
||||
|
||||
|
@ -177,6 +176,37 @@ func CreateServiceRoute(w http.ResponseWriter, req *http.Request) {
|
|||
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(w, req, serviceRequest)
|
||||
} else {
|
||||
utils.Error("CreateService: Method not allowed" + req.Method, nil)
|
||||
utils.HTTPError(w, "Method not allowed", http.StatusMethodNotAllowed, "HTTP001")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func CreateService(w http.ResponseWriter, req *http.Request, serviceRequest DockerServiceCreateRequest) error {
|
||||
// Enable streaming of response by setting appropriate headers
|
||||
w.Header().Set("X-Content-Type-Options", "nosniff")
|
||||
w.Header().Set("Transfer-Encoding", "chunked")
|
||||
|
||||
flusher, ok := w.(http.Flusher)
|
||||
if !ok {
|
||||
http.Error(w, "Streaming unsupported!", http.StatusInternalServerError)
|
||||
return errors.New("Streaming unsupported!")
|
||||
}
|
||||
|
||||
utils.ConfigLock.Lock()
|
||||
defer utils.ConfigLock.Unlock()
|
||||
|
||||
|
@ -187,16 +217,8 @@ func CreateServiceRoute(w http.ResponseWriter, req *http.Request) {
|
|||
config := utils.ReadConfigFromFile()
|
||||
configRoutes := config.HTTPConfig.ProxyConfig.Routes
|
||||
|
||||
decoder := json.NewDecoder(req.Body)
|
||||
var serviceRequest DockerServiceCreateRequest
|
||||
err := decoder.Decode(&serviceRequest)
|
||||
if err != nil {
|
||||
utils.Error("CreateService - decode - ", err)
|
||||
utils.HTTPError(w, "Bad request: "+err.Error(), http.StatusBadRequest, "DS003")
|
||||
return
|
||||
}
|
||||
|
||||
var rollbackActions []DockerServiceCreateRollback
|
||||
var err error
|
||||
|
||||
// Create networks
|
||||
for networkToCreateName, networkToCreate := range serviceRequest.Networks {
|
||||
|
@ -211,7 +233,7 @@ func CreateServiceRoute(w http.ResponseWriter, req *http.Request) {
|
|||
fmt.Fprintf(w, "[ERROR] Network %s already exists\n", networkToCreateName)
|
||||
flusher.Flush()
|
||||
Rollback(rollbackActions, w, flusher)
|
||||
return
|
||||
return err
|
||||
}
|
||||
|
||||
ipamConfig := make([]network.IPAMConfig, len(networkToCreate.IPAM.Config))
|
||||
|
@ -239,7 +261,7 @@ func CreateServiceRoute(w http.ResponseWriter, req *http.Request) {
|
|||
fmt.Fprintf(w, "[ERROR] Rolling back changes because of -- Network creation error: "+err.Error())
|
||||
flusher.Flush()
|
||||
Rollback(rollbackActions, w, flusher)
|
||||
return
|
||||
return err
|
||||
}
|
||||
|
||||
rollbackActions = append(rollbackActions, DockerServiceCreateRollback{
|
||||
|
@ -270,7 +292,7 @@ func CreateServiceRoute(w http.ResponseWriter, req *http.Request) {
|
|||
fmt.Fprintf(w, "[ERROR] Rolling back changes because of -- Volume creation error: "+err.Error())
|
||||
flusher.Flush()
|
||||
Rollback(rollbackActions, w, flusher)
|
||||
return
|
||||
return err
|
||||
}
|
||||
|
||||
rollbackActions = append(rollbackActions, DockerServiceCreateRollback{
|
||||
|
@ -298,7 +320,7 @@ func CreateServiceRoute(w http.ResponseWriter, req *http.Request) {
|
|||
fmt.Fprintf(w, "[ERROR] Rolling back changes because of -- Image pull error: "+err.Error())
|
||||
flusher.Flush()
|
||||
Rollback(rollbackActions, w, flusher)
|
||||
return
|
||||
return err
|
||||
}
|
||||
defer out.Close()
|
||||
|
||||
|
@ -376,7 +398,7 @@ func CreateServiceRoute(w http.ResponseWriter, req *http.Request) {
|
|||
fmt.Fprintf(w, "[ERROR] Unable to create directory for bind mount: "+err.Error())
|
||||
flusher.Flush()
|
||||
Rollback(rollbackActions, w, flusher)
|
||||
return
|
||||
return err
|
||||
}
|
||||
|
||||
if container.User != "" {
|
||||
|
@ -466,7 +488,7 @@ func CreateServiceRoute(w http.ResponseWriter, req *http.Request) {
|
|||
fmt.Fprintf(w, "[ERROR] Rolling back changes because of -- Container creation error: "+err.Error())
|
||||
flusher.Flush()
|
||||
Rollback(rollbackActions, w, flusher)
|
||||
return
|
||||
return err
|
||||
}
|
||||
|
||||
rollbackActions = append(rollbackActions, DockerServiceCreateRollback{
|
||||
|
@ -492,7 +514,7 @@ func CreateServiceRoute(w http.ResponseWriter, req *http.Request) {
|
|||
fmt.Fprintf(w, "[ERROR] Rolling back changes because of -- Route already exist")
|
||||
flusher.Flush()
|
||||
Rollback(rollbackActions, w, flusher)
|
||||
return
|
||||
return errors.New("Route already exist")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -509,7 +531,7 @@ func CreateServiceRoute(w http.ResponseWriter, req *http.Request) {
|
|||
fmt.Fprintf(w, "[ERROR] Rolling back changes because of -- Container creation error: "+err.Error())
|
||||
flusher.Flush()
|
||||
Rollback(rollbackActions, w, flusher)
|
||||
return
|
||||
return err
|
||||
}
|
||||
|
||||
// Start all the newly created containers
|
||||
|
@ -520,7 +542,7 @@ func CreateServiceRoute(w http.ResponseWriter, req *http.Request) {
|
|||
fmt.Fprintf(w, "[ERROR] Rolling back changes because of -- Container start error: "+err.Error())
|
||||
flusher.Flush()
|
||||
Rollback(rollbackActions, w, flusher)
|
||||
return
|
||||
return err
|
||||
}
|
||||
|
||||
// Write a response to the client
|
||||
|
@ -538,11 +560,8 @@ func CreateServiceRoute(w http.ResponseWriter, req *http.Request) {
|
|||
utils.Log("CreateService: Operation succeeded. SERVICE STARTED")
|
||||
fmt.Fprintf(w, "[OPERATION SUCCEEDED]. SERVICE STARTED\n")
|
||||
flusher.Flush()
|
||||
} else {
|
||||
utils.Error("CreateService: Method not allowed" + req.Method, nil)
|
||||
utils.HTTPError(w, "Method not allowed", http.StatusMethodNotAllowed, "HTTP001")
|
||||
return
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func ReOrderServices(serviceMap map[string]ContainerCreateRequestContainer) ([]ContainerCreateRequestContainer, error) {
|
||||
|
|
|
@ -68,7 +68,7 @@ func ManageContainerRoute(w http.ResponseWriter, req *http.Request) {
|
|||
case "unpause":
|
||||
err = DockerClient.ContainerUnpause(DockerContext, container.ID)
|
||||
case "recreate":
|
||||
_, err = EditContainer(container.ID, container)
|
||||
_, err = EditContainer(container.ID, container, false)
|
||||
case "update":
|
||||
out, errPull := DockerClient.ImagePull(DockerContext, imagename, doctype.ImagePullOptions{})
|
||||
if errPull != nil {
|
||||
|
@ -100,7 +100,7 @@ func ManageContainerRoute(w http.ResponseWriter, req *http.Request) {
|
|||
|
||||
utils.Log("Container Update - Image pulled " + imagename)
|
||||
|
||||
_, err = EditContainer(container.ID, container)
|
||||
_, err = EditContainer(container.ID, container, false)
|
||||
|
||||
if err != nil {
|
||||
utils.Error("Container Update - EditContainer", err)
|
||||
|
|
|
@ -20,7 +20,7 @@ func NewDBRoute(w http.ResponseWriter, req *http.Request) {
|
|||
}
|
||||
|
||||
if(req.Method == "GET") {
|
||||
costr, err := NewDB()
|
||||
costr, err := NewDB(w, req)
|
||||
|
||||
if err != nil {
|
||||
utils.Error("NewDB: Error while creating new DB", err)
|
||||
|
|
|
@ -37,9 +37,12 @@ func SecureContainerRoute(w http.ResponseWriter, req *http.Request) {
|
|||
"cosmos-force-network-secured": status,
|
||||
});
|
||||
|
||||
// change network mode to bridge in case it was set to container
|
||||
container.HostConfig.NetworkMode = "bridge"
|
||||
|
||||
utils.Log("API: Set Force network secured "+status+" : " + containerName)
|
||||
|
||||
_, errEdit := EditContainer(container.ID, container)
|
||||
_, errEdit := EditContainer(container.ID, container, false)
|
||||
if errEdit != nil {
|
||||
utils.Error("ContainerSecureEdit", errEdit)
|
||||
utils.HTTPError(w, "Internal server error: " + errEdit.Error(), http.StatusInternalServerError, "DS003")
|
||||
|
|
|
@ -23,6 +23,7 @@ type ContainerForm struct {
|
|||
Volumes []mount.Mount `json:"Volumes"`
|
||||
// we make this a int so that we can ignore 0
|
||||
Interactive int `json:"interactive"`
|
||||
NetworkMode string `json:"networkMode"`
|
||||
}
|
||||
|
||||
func UpdateContainerRoute(w http.ResponseWriter, req *http.Request) {
|
||||
|
@ -119,8 +120,11 @@ func UpdateContainerRoute(w http.ResponseWriter, req *http.Request) {
|
|||
container.Config.Tty = form.Interactive == 2
|
||||
container.Config.OpenStdin = form.Interactive == 2
|
||||
}
|
||||
if(form.NetworkMode != "") {
|
||||
container.HostConfig.NetworkMode = containerType.NetworkMode(form.NetworkMode)
|
||||
}
|
||||
|
||||
_, err = EditContainer(container.ID, container)
|
||||
_, err = EditContainer(container.ID, container, false)
|
||||
if err != nil {
|
||||
utils.Error("UpdateContainer: EditContainer", err)
|
||||
utils.HTTPError(w, "Internal server error: "+err.Error(), http.StatusInternalServerError, "DS004")
|
||||
|
|
|
@ -31,6 +31,13 @@ func BootstrapAllContainersFromTags() []error {
|
|||
return errors
|
||||
}
|
||||
|
||||
func UnsecureContainer(container types.ContainerJSON) (string, error) {
|
||||
RemoveLabels(container, []string{
|
||||
"cosmos-force-network-secured",
|
||||
});
|
||||
return EditContainer(container.ID, container, false)
|
||||
}
|
||||
|
||||
func BootstrapContainerFromTags(containerID string) error {
|
||||
errD := Connect()
|
||||
if errD != nil {
|
||||
|
@ -71,6 +78,12 @@ func BootstrapContainerFromTags(containerID string) error {
|
|||
if !isCosmosCon {
|
||||
needsRestart, errCT = ConnectToSecureNetwork(container)
|
||||
if errCT != nil {
|
||||
utils.Warn("DockerContainerBootstrapConnectToSecureNetwork -- Cannot connect to network, removing force secure")
|
||||
_, errUn := UnsecureContainer(container)
|
||||
if errUn != nil {
|
||||
utils.Fatal("DockerContainerBootstrapUnsecureContainer -- A broken container state is preventing Cosmos from functionning. Please remove the cosmos-force-secure label from the container "+container.Name+" manually", errUn)
|
||||
return errCT
|
||||
}
|
||||
return errCT
|
||||
}
|
||||
if needsRestart {
|
||||
|
@ -84,7 +97,12 @@ func BootstrapContainerFromTags(containerID string) error {
|
|||
utils.Log(container.Name+": Disconnecting from bridge network")
|
||||
errDisc := DockerClient.NetworkDisconnect(DockerContext, "bridge", containerID, true)
|
||||
if errDisc != nil {
|
||||
utils.Error("Docker Network Disconnect", errDisc)
|
||||
utils.Warn("DockerContainerBootstrapDisconnectFromBridge -- Cannot disconnect from Bridge, removing force secure")
|
||||
_, errUn := UnsecureContainer(container)
|
||||
if errUn != nil {
|
||||
utils.Fatal("DockerContainerBootstrapUnsecureContainer -- A broken container state is preventing Cosmos from functionning. Please remove the cosmos-force-secure label from the container "+container.Name+" manually", errUn)
|
||||
return errDisc
|
||||
}
|
||||
return errDisc
|
||||
}
|
||||
}
|
||||
|
@ -99,7 +117,7 @@ func BootstrapContainerFromTags(containerID string) error {
|
|||
}
|
||||
|
||||
if(needsUpdate) {
|
||||
_, errEdit := EditContainer(containerID, container)
|
||||
_, errEdit := EditContainer(containerID, container, false)
|
||||
if errEdit != nil {
|
||||
utils.Error("Docker Boostrap, couldn't update container: ", errEdit)
|
||||
return errEdit
|
||||
|
|
|
@ -4,7 +4,6 @@ import (
|
|||
"context"
|
||||
"errors"
|
||||
"time"
|
||||
"fmt"
|
||||
"bufio"
|
||||
"strings"
|
||||
"github.com/azukaar/cosmos-server/src/utils"
|
||||
|
@ -82,11 +81,8 @@ func Connect() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func EditContainer(oldContainerID string, newConfig types.ContainerJSON) (string, error) {
|
||||
|
||||
utils.Debug("VOLUMES:" + fmt.Sprintf("%v", newConfig.HostConfig.Mounts))
|
||||
|
||||
if(oldContainerID != "") {
|
||||
func EditContainer(oldContainerID string, newConfig types.ContainerJSON, noLock bool) (string, error) {
|
||||
if(oldContainerID != "" && !noLock) {
|
||||
// no need to re-lock if we are reverting
|
||||
DockerNetworkLock <- true
|
||||
defer func() {
|
||||
|
@ -100,6 +96,17 @@ func EditContainer(oldContainerID string, newConfig types.ContainerJSON) (string
|
|||
}
|
||||
}
|
||||
|
||||
if(newConfig.HostConfig.NetworkMode != "bridge" &&
|
||||
newConfig.HostConfig.NetworkMode != "default" &&
|
||||
newConfig.HostConfig.NetworkMode != "host" &&
|
||||
newConfig.HostConfig.NetworkMode != "none") {
|
||||
if(!HasLabel(newConfig, "cosmos-force-network-mode")) {
|
||||
AddLabels(newConfig, map[string]string{"cosmos-force-network-mode": string(newConfig.HostConfig.NetworkMode)})
|
||||
} else {
|
||||
newConfig.HostConfig.NetworkMode = container.NetworkMode(GetLabel(newConfig, "cosmos-force-network-mode"))
|
||||
}
|
||||
}
|
||||
|
||||
newName := newConfig.Name
|
||||
oldContainer := newConfig
|
||||
|
||||
|
@ -112,8 +119,6 @@ func EditContainer(oldContainerID string, newConfig types.ContainerJSON) (string
|
|||
// https://godoc.org/github.com/docker/docker/api/types#ContainerJSON
|
||||
oldContainer, err = DockerClient.ContainerInspect(DockerContext, oldContainerID)
|
||||
|
||||
utils.Debug("OLD VOLUMES:" + fmt.Sprintf("%v", oldContainer.HostConfig.Mounts))
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
@ -138,7 +143,6 @@ func EditContainer(oldContainerID string, newConfig types.ContainerJSON) (string
|
|||
|
||||
// if no name, use the same one, that will force Docker to create a hostname if not set
|
||||
newName = oldContainer.Name
|
||||
newConfig.Config.Hostname = newName
|
||||
|
||||
// stop and remove container
|
||||
stopError := DockerClient.ContainerStop(DockerContext, oldContainerID, container.StopOptions{})
|
||||
|
@ -168,6 +172,16 @@ func EditContainer(oldContainerID string, newConfig types.ContainerJSON) (string
|
|||
utils.Log("EditContainer - Revert started")
|
||||
}
|
||||
|
||||
// only force hostname if network is bridge or default, otherwise it will fail
|
||||
if newConfig.HostConfig.NetworkMode == "bridge" || newConfig.HostConfig.NetworkMode == "default" {
|
||||
newConfig.Config.Hostname = newName
|
||||
} else {
|
||||
// if not, remove hostname because otherwise it will try to keep the old one
|
||||
newConfig.Config.Hostname = ""
|
||||
// IDK Docker is weird, if you don't erase this it will break
|
||||
newConfig.Config.ExposedPorts = nil
|
||||
}
|
||||
|
||||
// recreate container with new informations
|
||||
createResponse, createError := DockerClient.ContainerCreate(
|
||||
DockerContext,
|
||||
|
@ -177,6 +191,9 @@ func EditContainer(oldContainerID string, newConfig types.ContainerJSON) (string
|
|||
nil,
|
||||
newName,
|
||||
)
|
||||
if createError != nil {
|
||||
utils.Error("EditContainer - Failed to create container", createError)
|
||||
}
|
||||
|
||||
utils.Log("EditContainer - Container recreated. Re-connecting networks " + createResponse.ID)
|
||||
|
||||
|
@ -202,6 +219,10 @@ func EditContainer(oldContainerID string, newConfig types.ContainerJSON) (string
|
|||
|
||||
runError := DockerClient.ContainerStart(DockerContext, createResponse.ID, types.ContainerStartOptions{})
|
||||
|
||||
if runError != nil {
|
||||
utils.Error("EditContainer - Failed to run container", runError)
|
||||
}
|
||||
|
||||
if createError != nil || runError != nil {
|
||||
if(oldContainerID == "") {
|
||||
if(createError == nil) {
|
||||
|
@ -217,12 +238,17 @@ func EditContainer(oldContainerID string, newConfig types.ContainerJSON) (string
|
|||
|
||||
if(createError == nil) {
|
||||
utils.Log("EditContainer - Killing new broken container")
|
||||
// attempt kill
|
||||
DockerClient.ContainerKill(DockerContext, oldContainerID, "")
|
||||
DockerClient.ContainerKill(DockerContext, createResponse.ID, "")
|
||||
// attempt remove in case created state
|
||||
DockerClient.ContainerRemove(DockerContext, oldContainerID, types.ContainerRemoveOptions{})
|
||||
DockerClient.ContainerRemove(DockerContext, createResponse.ID, types.ContainerRemoveOptions{})
|
||||
}
|
||||
|
||||
utils.Log("EditContainer - Reverting...")
|
||||
// attempt to restore container
|
||||
restored, restoreError := EditContainer("", oldContainer)
|
||||
restored, restoreError := EditContainer("", oldContainer, false)
|
||||
|
||||
if restoreError != nil {
|
||||
utils.Error("EditContainer - Failed to restore container", restoreError)
|
||||
|
@ -246,11 +272,47 @@ func EditContainer(oldContainerID string, newConfig types.ContainerJSON) (string
|
|||
}
|
||||
}
|
||||
|
||||
// Recreating dependant containers
|
||||
utils.Debug("Unlocking EDIT Container")
|
||||
|
||||
if oldContainerID != "" {
|
||||
RecreateDepedencies(oldContainerID)
|
||||
}
|
||||
|
||||
utils.Log("EditContainer - Container started. All done! " + createResponse.ID)
|
||||
|
||||
return createResponse.ID, nil
|
||||
}
|
||||
|
||||
func RecreateDepedencies(containerID string) {
|
||||
containers, err := ListContainers()
|
||||
if err != nil {
|
||||
utils.Error("RecreateDepedencies", err)
|
||||
return
|
||||
}
|
||||
|
||||
for _, container := range containers {
|
||||
if container.ID == containerID {
|
||||
continue
|
||||
}
|
||||
|
||||
fullContainer, err := DockerClient.ContainerInspect(DockerContext, container.ID)
|
||||
if err != nil {
|
||||
utils.Error("RecreateDepedencies", err)
|
||||
continue
|
||||
}
|
||||
|
||||
// check if network mode contains containerID
|
||||
if strings.Contains(string(fullContainer.HostConfig.NetworkMode), containerID) {
|
||||
utils.Log("RecreateDepedencies - Recreating " + container.Names[0])
|
||||
_, err := EditContainer(container.ID, fullContainer, true)
|
||||
if err != nil {
|
||||
utils.Error("RecreateDepedencies - Failed to update - ", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func ListContainers() ([]types.Container, error) {
|
||||
errD := Connect()
|
||||
if errD != nil {
|
||||
|
@ -397,7 +459,7 @@ func CheckUpdatesAvailable() map[string]bool {
|
|||
|
||||
if needsUpdate && IsLabel(fullContainer, "cosmos-auto-update") {
|
||||
utils.Log("Downlaoded new update for " + container.Image + " ready to install")
|
||||
_, err := EditContainer(container.ID, fullContainer)
|
||||
_, err := EditContainer(container.ID, fullContainer, false)
|
||||
if err != nil {
|
||||
utils.Error("CheckUpdatesAvailable - Failed to update - ", err)
|
||||
} else {
|
||||
|
|
|
@ -4,6 +4,9 @@ import (
|
|||
"github.com/azukaar/cosmos-server/src/utils"
|
||||
"io"
|
||||
"os"
|
||||
"net/http"
|
||||
|
||||
|
||||
// "github.com/docker/docker/client"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/api/types"
|
||||
|
@ -18,13 +21,13 @@ type VolumeMount struct {
|
|||
Volume *types.Volume
|
||||
}
|
||||
|
||||
func NewDB() (string, error) {
|
||||
func NewDB(w http.ResponseWriter, req *http.Request) (string, error) {
|
||||
id := utils.GenerateRandomString(3)
|
||||
mongoUser := "cosmos-" + utils.GenerateRandomString(5)
|
||||
mongoPass := utils.GenerateRandomString(24)
|
||||
monHost := "cosmos-mongo-" + id
|
||||
|
||||
imageName := "mongo:latest"
|
||||
imageName := "mongo:5"
|
||||
|
||||
// if CPU is missing AVX, use 4.4
|
||||
if runtime.GOARCH == "amd64" && !cpu.X86.HasAVX {
|
||||
|
@ -32,28 +35,36 @@ func NewDB() (string, error) {
|
|||
imageName = "mongo:4.4"
|
||||
}
|
||||
|
||||
err := RunContainer(
|
||||
imageName,
|
||||
monHost,
|
||||
[]string{
|
||||
service := DockerServiceCreateRequest{
|
||||
Services: map[string]ContainerCreateRequestContainer {},
|
||||
}
|
||||
|
||||
service.Services[monHost] = ContainerCreateRequestContainer{
|
||||
Name: monHost,
|
||||
Image: imageName,
|
||||
RestartPolicy: "always",
|
||||
Environment: []string{
|
||||
"MONGO_INITDB_ROOT_USERNAME=" + mongoUser,
|
||||
"MONGO_INITDB_ROOT_PASSWORD=" + mongoPass,
|
||||
},
|
||||
[]VolumeMount{
|
||||
{
|
||||
Destination: "/data/db",
|
||||
Volume: &types.Volume{
|
||||
Name: "cosmos-mongo-data-" + id,
|
||||
Labels: map[string]string{
|
||||
"cosmos-force-network-secured": "true",
|
||||
},
|
||||
Volumes: []mount.Mount{
|
||||
{
|
||||
Type: mount.TypeVolume,
|
||||
Source: "cosmos-mongo-data-" + id,
|
||||
Target: "/data/db",
|
||||
},
|
||||
{
|
||||
Destination: "/data/configdb",
|
||||
Volume: &types.Volume{
|
||||
Name: "cosmos-mongo-config-" + id,
|
||||
Type: mount.TypeVolume,
|
||||
Source: "cosmos-mongo-config-" + id,
|
||||
Target: "/data/configdb",
|
||||
},
|
||||
},
|
||||
},
|
||||
)
|
||||
};
|
||||
|
||||
err := CreateService(w, req, service)
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
|
@ -87,54 +98,13 @@ func RunContainer(imagename string, containername string, inputEnv []string, vol
|
|||
mounts = append(mounts, mount)
|
||||
}
|
||||
|
||||
// Define a PORT opening
|
||||
// newport, err := natting.NewPort("tcp", port)
|
||||
// if err != nil {
|
||||
// fmt.Println("Unable to create docker port")
|
||||
// return err
|
||||
// }
|
||||
|
||||
// Configured hostConfig:
|
||||
// https://godoc.org/github.com/docker/docker/api/types/container#HostConfig
|
||||
hostConfig := &container.HostConfig{
|
||||
// PortBindings: natting.PortMap{
|
||||
// newport: []natting.PortBinding{
|
||||
// {
|
||||
// HostIP: "0.0.0.0",
|
||||
// HostPort: port,
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
Mounts : mounts,
|
||||
RestartPolicy: container.RestartPolicy{
|
||||
Name: "always",
|
||||
},
|
||||
// LogConfig: container.LogConfig{
|
||||
// Type: "json-file",
|
||||
// Config: map[string]string{},
|
||||
// },
|
||||
}
|
||||
|
||||
// Define Network config
|
||||
// https://godoc.org/github.com/docker/docker/api/types/network#NetworkingConfig
|
||||
|
||||
|
||||
|
||||
// networkConfig := &network.NetworkingConfig{
|
||||
// EndpointsConfig: map[string]*network.EndpointSettings{},
|
||||
// }
|
||||
// gatewayConfig := &network.EndpointSettings{
|
||||
// Gateway: "gatewayname",
|
||||
// }
|
||||
// networkConfig.EndpointsConfig["bridge"] = gatewayConfig
|
||||
|
||||
// Define ports to be exposed (has to be same as hostconfig.portbindings.newport)
|
||||
// exposedPorts := map[natting.Port]struct{}{
|
||||
// newport: struct{}{},
|
||||
// }
|
||||
|
||||
// Configuration
|
||||
// https://godoc.org/github.com/docker/docker/api/types/container#Config
|
||||
config := &container.Config{
|
||||
Image: imagename,
|
||||
Env: inputEnv,
|
||||
|
@ -145,9 +115,6 @@ func RunContainer(imagename string, containername string, inputEnv []string, vol
|
|||
// ExposedPorts: exposedPorts,
|
||||
}
|
||||
|
||||
//archi := runtime.GOARCH
|
||||
|
||||
// Creating the actual container. This is "nil,nil,nil" in every example.
|
||||
cont, err := DockerClient.ContainerCreate(
|
||||
DockerContext,
|
||||
config,
|
||||
|
@ -162,7 +129,6 @@ func RunContainer(imagename string, containername string, inputEnv []string, vol
|
|||
return err
|
||||
}
|
||||
|
||||
// Run the actual container
|
||||
DockerClient.ContainerStart(DockerContext, cont.ID, types.ContainerStartOptions{})
|
||||
utils.Log("Container created " + cont.ID)
|
||||
|
||||
|
|
|
@ -190,7 +190,7 @@ func StartServer() {
|
|||
|
||||
// need rewrite bc it catches too many things and prevent
|
||||
// client to be notified of the error
|
||||
// router.Use(middleware.Recoverer)
|
||||
|
||||
router.Use(middleware.Logger)
|
||||
router.Use(utils.SetSecurityHeaders)
|
||||
|
||||
|
|
12
src/icons.go
12
src/icons.go
|
@ -123,7 +123,7 @@ func GetFavicon(w http.ResponseWriter, req *http.Request) {
|
|||
iconURL := icon.URL
|
||||
u, err := url.Parse(siteurl)
|
||||
if err != nil {
|
||||
utils.Error("FaviconFetch failed to parse ", err)
|
||||
utils.Debug("FaviconFetch failed to parse " + err.Error())
|
||||
continue
|
||||
}
|
||||
|
||||
|
@ -146,21 +146,21 @@ func GetFavicon(w http.ResponseWriter, req *http.Request) {
|
|||
}
|
||||
}
|
||||
|
||||
utils.Log("Favicon Trying to fetch " + iconURL)
|
||||
utils.Debug("Favicon Trying to fetch " + iconURL)
|
||||
|
||||
// Fetch the favicon
|
||||
resp, err := http.Get(iconURL)
|
||||
if err != nil {
|
||||
utils.Error("FaviconFetch", err)
|
||||
utils.Debug("FaviconFetch" + err.Error())
|
||||
continue
|
||||
}
|
||||
|
||||
// check if 200 and if image
|
||||
if resp.StatusCode != 200 {
|
||||
utils.Error("FaviconFetch - " + iconURL + " - not 200 ", nil)
|
||||
utils.Debug("FaviconFetch - " + iconURL + " - not 200 ")
|
||||
continue
|
||||
} else if !strings.Contains(resp.Header.Get("Content-Type"), "image") && !strings.Contains(resp.Header.Get("Content-Type"), "octet-stream") {
|
||||
utils.Error("FaviconFetch - " + iconURL + " - not image ", nil)
|
||||
utils.Debug("FaviconFetch - " + iconURL + " - not image ")
|
||||
continue
|
||||
} else {
|
||||
utils.Log("Favicon found " + iconURL)
|
||||
|
@ -168,7 +168,7 @@ func GetFavicon(w http.ResponseWriter, req *http.Request) {
|
|||
// Cache the response
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
utils.Error("FaviconFetch - cant read ", err)
|
||||
utils.Debug("FaviconFetch - cant read " + err.Error())
|
||||
continue
|
||||
}
|
||||
|
||||
|
|
|
@ -85,13 +85,13 @@ func NewInstallRoute(w http.ResponseWriter, req *http.Request) {
|
|||
} else if (request.MongoDBMode == "Create") {
|
||||
utils.Log("NewInstall: Create DB")
|
||||
newConfig.DisableUserManagement = false
|
||||
strco, err := docker.NewDB()
|
||||
|
||||
strco, err := docker.NewDB(w, req)
|
||||
if err != nil {
|
||||
utils.Error("NewInstall: Error creating MongoDB", err)
|
||||
utils.HTTPError(w, "New Install: Error creating MongoDB " + err.Error(),
|
||||
http.StatusInternalServerError, "NI001")
|
||||
return
|
||||
}
|
||||
|
||||
newConfig.MongoDB = strco
|
||||
utils.SaveConfigTofile(newConfig)
|
||||
utils.LoadBaseMainConfig(newConfig)
|
||||
|
|
Loading…
Reference in a new issue