[release] version 0.5.5

This commit is contained in:
Yann Stepienik 2023-05-19 16:23:05 +01:00
parent c8731e2fa7
commit a6098f0507
23 changed files with 654 additions and 484 deletions

View file

@ -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';

View 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>
);

View file

@ -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"

View file

@ -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);

View file

@ -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;
}
}
});
}

View file

@ -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);

View file

@ -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));

View file

@ -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>

View file

@ -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);

View file

@ -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

View file

@ -1,6 +1,6 @@
{
"name": "cosmos-server",
"version": "0.5.4",
"version": "0.5.5",
"description": "",
"main": "test-server.js",
"bugs": {

View file

@ -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")

View file

@ -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) {

View file

@ -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)

View file

@ -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)

View file

@ -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")

View file

@ -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")

View file

@ -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

View file

@ -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 {

View file

@ -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)

View file

@ -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)

View file

@ -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
}

View file

@ -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)