[release] v0.5.12-unstable

This commit is contained in:
Yann Stepienik 2023-05-27 18:11:33 +01:00
parent 95fe730985
commit 3a228a9831
19 changed files with 262 additions and 200 deletions

View file

@ -11,39 +11,8 @@ jobs:
name: install dependencies
command: sudo apt-get install bash curl
- run:
name: download Go
command: wget https://golang.org/dl/go1.20.2.linux-amd64.tar.gz
- run:
name: install Go
command: sudo rm -rf /usr/local/go && sudo tar -C /usr/local -xzf go1.20.2.linux-amd64.tar.gz
- run:
name: set Go path
command: echo 'export PATH=$PATH:/usr/local/go/bin' >> $BASH_ENV
- run: |
echo 'export NVM_DIR="/opt/circleci/.nvm"' >> $BASH_ENV
echo ' [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"' >> $BASH_ENV
- run: |
node -v
- run: |
nvm install v16
node -v
nvm alias default v16
- run: |
node -v
- run: docker login -u $DOCKER_LOGIN -p $DOCKER_PASSWORD
- run:
name: Install dependencies
command: npm install
- run:
name: Download GeoLite2-Country database
command: |
@ -70,49 +39,14 @@ jobs:
name: install dependencies
command: sudo apt-get install bash curl
- run:
name: download Go
command: wget https://golang.org/dl/go1.20.2.linux-arm64.tar.gz
- run:
name: install Go
command: sudo rm -rf /usr/local/go && sudo tar -C /usr/local -xzf go1.20.2.linux-arm64.tar.gz
- run:
name: set Go path
command: echo 'export PATH=$PATH:/usr/local/go/bin' >> $BASH_ENV
- run: |
echo 'export NVM_DIR="/opt/circleci/.nvm"' >> $BASH_ENV
echo ' [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"' >> $BASH_ENV
- run: |
node -v
- run: |
nvm install v16
node -v
nvm alias default v16
- run: |
node -v
- run: docker login -u $DOCKER_LOGIN -p $DOCKER_PASSWORD
- run:
name: Install dependencies
command: npm install
- run:
name: Download GeoLite2-Country database
command: |
curl -s -L "https://download.maxmind.com/app/geoip_download?edition_id=GeoLite2-Country&license_key=$MAX_TOKEN&suffix=tar.gz" -o GeoLite2-Country.tar.gz
tar -xzf GeoLite2-Country.tar.gz --strip-components 1 --wildcards "*.mmdb"
- run:
name: Build UI
command: npm run client-build
- run:
name: Build and publish dockerfiles
command: sh docker.arm64.sh

3
.dockerignore Normal file
View file

@ -0,0 +1,3 @@
node_modules
static
build

View file

@ -39,12 +39,19 @@ const GetActions = ({
let actions = [
{
t: 'Update Available',
t: 'Update Available, Click to Update',
if: ['update_available'],
e: <IconButton className="shinyButton" color='primary' onClick={() => {doTo('update')}} size={isMiniMobile ? 'medium' : 'large'}>
<UpCircleOutlined />
</IconButton>
},
{
t: 'No Update Available. Click to Force Pull',
if: ['update_not_available'],
e: <IconButton onClick={() => {doTo('update')}} size={isMiniMobile ? 'medium' : 'large'}>
<UpCircleOutlined />
</IconButton>
},
{
t: 'Start',
if: ['exited', 'created'],
@ -117,7 +124,7 @@ const GetActions = ({
/>
{!isUpdating && actions.filter((action) => {
return action.if.includes(state) || (updateAvailable && action.if.includes('update_available'));
return action.if.includes(state) || (updateAvailable && action.if.includes('update_available')) || (!updateAvailable && action.if.includes('update_not_available'));
}).map((action) => {
return <Tooltip title={action.t}>{action.e}</Tooltip>
})}

View file

@ -22,6 +22,8 @@ const ContainerIndex = () => {
const { containerName } = useParams();
const [container, setContainer] = React.useState(null);
const [config, setConfig] = React.useState(null);
const [selfName, setSelfName] = React.useState("");
const [updatesAvailable, setUpdatesAvailable] = React.useState(null);
const refreshContainer = () => {
return Promise.all([API.docker.get(containerName).then((res) => {
@ -29,6 +31,8 @@ const ContainerIndex = () => {
}),
API.config.get().then((res) => {
setConfig(res.data);
setUpdatesAvailable(res.updates);
setSelfName(res.hostname);
})]);
};
@ -49,7 +53,7 @@ const ContainerIndex = () => {
tabs={[
{
title: 'Overview',
children: <ContainerOverview refresh={refreshContainer} containerInfo={container} config={config}/>
children: <ContainerOverview updatesAvailable={updatesAvailable} selfName={selfName} refresh={refreshContainer} containerInfo={container} config={config}/>
},
{
title: 'Logs',

View file

@ -15,7 +15,7 @@ const info = {
borderRadius: '5px',
}
const ContainerOverview = ({ containerInfo, config, refresh }) => {
const ContainerOverview = ({ containerInfo, config, refresh, updatesAvailable, selfName }) => {
const isMobile = useMediaQuery((theme) => theme.breakpoints.down('sm'));
const [openModal, setOpenModal] = React.useState(false);
const [openRestartModal, setOpenRestartModal] = React.useState(false);
@ -99,6 +99,7 @@ const ContainerOverview = ({ containerInfo, config, refresh }) => {
setIsUpdatingId={() => {
setIsUpdating(true);
}}
updateAvailable={updatesAvailable && updatesAvailable[Name]}
/>
</Stack>
{containerInfo.State.Status !== 'running' && (
@ -133,7 +134,8 @@ const ContainerOverview = ({ containerInfo, config, refresh }) => {
</Stack>
<Stack style={{ fontSize: '80%' }} direction={"row"} alignItems="center">
<Checkbox
checked={Config.Labels['cosmos-auto-update'] === 'true'}
checked={Config.Labels['cosmos-auto-update'] === 'true' ||
(selfName && Name.replace('/', '') == selfName && config.AutoUpdate)}
disabled={isUpdating}
onChange={(e) => {
setIsUpdating(true);

View file

@ -45,6 +45,7 @@ const ServeApps = () => {
const [newRoute, setNewRoute] = useState(null);
const [submitErrors, setSubmitErrors] = useState([]);
const [openRestartModal, setOpenRestartModal] = useState(false);
const [selfName, setSelfName] = useState("");
const refreshServeApps = () => {
API.docker.list().then((res) => {
@ -53,6 +54,7 @@ const ServeApps = () => {
API.config.get().then((res) => {
setConfig(res.data);
setUpdatesAvailable(res.updates);
setSelfName(res.hostname);
});
setIsUpdating({});
};
@ -194,7 +196,7 @@ const ServeApps = () => {
</Stack>
</Stack>
</Stack>
<Stack direction="row" spacing={2} width='100%'>
<Stack direction="row" spacing={1} width='100%'>
<GetActions
Id={app.Names[0].replace('/', '')}
image={app.Image}
@ -256,7 +258,8 @@ const ServeApps = () => {
</Stack>
<Stack style={{ fontSize: '80%' }} direction={"row"} alignItems="center">
<Checkbox
checked={app.Labels['cosmos-auto-update'] === 'true'}
checked={app.Labels['cosmos-auto-update'] === 'true' ||
(selfName && app.Names[0].replace('/', '') == selfName && config.AutoUpdate)}
disabled={app.State !== 'running'}
onChange={(e) => {
const name = app.Names[0].replace('/', '');

View file

@ -12,8 +12,6 @@ fi
echo "Pushing azukaar/cosmos-server:$VERSION and azukaar/cosmos-server:$LATEST"
sh build.arm64.sh
docker build \
-t azukaar/cosmos-server:$VERSION-arm64 \
-t azukaar/cosmos-server:$LATEST-arm64 \

View file

@ -12,8 +12,6 @@ fi
echo "Pushing azukaar/cosmos-server:$VERSION and azukaar/cosmos-server:$LATEST"
sh build.sh
docker build \
-t azukaar/cosmos-server:$VERSION \
-t azukaar/cosmos-server:$LATEST \

View file

@ -10,11 +10,29 @@ RUN apt-get update && apt-get install -y ca-certificates openssl
WORKDIR /app
COPY build/cosmos .
COPY build/cosmos_gray.png .
COPY build/Logo.png .
COPY build/GeoLite2-Country.mmdb .
COPY build/meta.json .
COPY static ./static
# install go 1.20.2
RUN apt-get install -y wget curl
RUN wget https://golang.org/dl/go1.20.2.linux-amd64.tar.gz
RUN tar -C /usr/local -xzf go1.20.2.linux-amd64.tar.gz
ENV PATH=$PATH:/usr/local/go/bin
# install nodejs 18.16.0
RUN curl -fsSL https://deb.nodesource.com/setup_18.x | bash -
RUN apt-get install -y nodejs
COPY go.mod ./
COPY go.sum ./
RUN go mod download
COPY package.json ./
COPY package-lock.json ./
RUN npm install
COPY . .
RUN ls
RUN npm run client-build
RUN ./build.sh
WORKDIR /app/build
CMD ["./cosmos"]

View file

@ -6,17 +6,33 @@ EXPOSE 443 80
VOLUME /config
RUN apt-get clean
RUN apt-get update
RUN apt-get install -y ca-certificates openssl
RUN apt-get update && apt-get install -y ca-certificates openssl
WORKDIR /app
COPY build/cosmos .
COPY build/cosmos_gray.png .
COPY build/Logo.png .
COPY build/GeoLite2-Country.mmdb .
COPY build/meta.json .
COPY static ./static
# install go 1.20.2
RUN apt-get install -y wget curl
RUN wget https://golang.org/dl/go1.20.2.linux-amd64.tar.gz
RUN tar -C /usr/local -xzf go1.20.2.linux-amd64.tar.gz
ENV PATH=$PATH:/usr/local/go/bin
# install nodejs 18.16.0
RUN curl -fsSL https://deb.nodesource.com/setup_18.x | bash -
RUN apt-get install -y nodejs
COPY go.mod ./
COPY go.sum ./
RUN go mod download
COPY package.json ./
COPY package-lock.json ./
RUN npm install
COPY . .
RUN ls
RUN npm run client-build
RUN ./build.sh
WORKDIR /app/build
CMD ["./cosmos"]

View file

@ -1,6 +1,6 @@
{
"name": "cosmos-server",
"version": "0.5.11",
"version": "0.5.12-unstable",
"description": "",
"main": "test-server.js",
"bugs": {
@ -61,6 +61,7 @@
"dockerdevbuild": "sh build.sh && docker build --tag cosmos-dev .",
"dockerdevrun": "docker stop cosmos-dev; docker rm cosmos-dev; docker run -d -p 80:80 -p 443:443 -e DOCKER_HOST=tcp://host.docker.internal:2375 -e COSMOS_MONGODB=$MONGODB -e COSMOS_LOG_LEVEL=DEBUG -v /:/mnt/host --restart=unless-stopped -h cosmos-dev --name cosmos-dev cosmos-dev",
"dockerdev": "npm run dockerdevbuild && npm run dockerdevrun",
"dockerdevclient": "npm run client-build && npm run dockerdevbuild && npm run dockerdevrun",
"demo": "vite build --base=/ui/ --mode demo",
"devdemo": "vite --mode demo"
},

View file

@ -3,6 +3,7 @@ package configapi
import (
"net/http"
"encoding/json"
"os"
"github.com/azukaar/cosmos-server/src/utils"
)
@ -40,6 +41,7 @@ func ConfigApiGet(w http.ResponseWriter, req *http.Request) {
"status": "OK",
"data": config,
"updates": utils.UpdateAvailable,
"hostname": os.Getenv("HOSTNAME"),
})
} else {
utils.Error("SettingGet: Method not allowed" + req.Method, nil)

View file

@ -20,8 +20,13 @@ func AutoUpdateContainerRoute(w http.ResponseWriter, req *http.Request) {
status := utils.Sanitize(vars["status"])
if os.Getenv("HOSTNAME") != "" && containerName == os.Getenv("HOSTNAME") {
utils.Error("AutoUpdateContainerRoute - Container cannot update itself", nil)
utils.HTTPError(w, "Container cannot update itself", http.StatusBadRequest, "DS003")
config := utils.ReadConfigFromFile()
config.AutoUpdate = status == "true"
utils.SaveConfigTofile(config)
utils.Log("API: Set Auto Update "+status+" : " + containerName)
json.NewEncoder(w).Encode(map[string]interface{}{
"status": "OK",
})
return
}

View file

@ -115,7 +115,7 @@ type DockerServiceCreateRollback struct {
Name string `json:"name"`
}
func Rollback(actions []DockerServiceCreateRollback , w http.ResponseWriter, flusher http.Flusher) {
func Rollback(actions []DockerServiceCreateRollback , OnLog func(string)) {
for i := len(actions) - 1; i >= 0; i-- {
action := actions[i]
switch action.Type {
@ -126,8 +126,7 @@ func Rollback(actions []DockerServiceCreateRollback , w http.ResponseWriter, flu
utils.Error("Rollback: Container", err)
} else {
utils.Log(fmt.Sprintf("Rolled back container %s", action.Name))
fmt.Fprintf(w, "Rolled back container %s\n", action.Name)
flusher.Flush()
OnLog(fmt.Sprintf("Rolled back container %s\n", action.Name))
}
case "volume":
err := DockerClient.VolumeRemove(DockerContext, action.Name, true)
@ -135,8 +134,7 @@ func Rollback(actions []DockerServiceCreateRollback , w http.ResponseWriter, flu
utils.Error("Rollback: Volume", err)
} else {
utils.Log(fmt.Sprintf("Rolled back volume %s", action.Name))
fmt.Fprintf(w, "Rolled back volume %s\n", action.Name)
flusher.Flush()
OnLog(fmt.Sprintf("Rolled back volume %s\n", action.Name))
}
case "network":
if os.Getenv("HOSTNAME") != "" {
@ -147,16 +145,14 @@ func Rollback(actions []DockerServiceCreateRollback , w http.ResponseWriter, flu
utils.Error("Rollback: Network", err)
} else {
utils.Log(fmt.Sprintf("Rolled back network %s", action.Name))
fmt.Fprintf(w, "Rolled back network %s\n", action.Name)
flusher.Flush()
OnLog(fmt.Sprintf("Rolled back network %s\n", action.Name))
}
}
}
// After all operations
utils.Error("CreateService", fmt.Errorf("Operation failed. Changes have been rolled back."))
fmt.Fprintf(w, "[OPERATION FAILED]. CHANGES HAVE BEEN ROLLEDBACK.\n")
flusher.Flush()
OnLog("[OPERATION FAILED]. CHANGES HAVE BEEN ROLLEDBACK.\n")
}
func CreateServiceRoute(w http.ResponseWriter, req *http.Request) {
@ -192,7 +188,12 @@ func CreateServiceRoute(w http.ResponseWriter, req *http.Request) {
return
}
CreateService(w, req, serviceRequest)
CreateService(serviceRequest,
func (msg string) {
fmt.Fprintf(w, msg)
flusher.Flush()
},
)
} else {
utils.Error("CreateService: Method not allowed" + req.Method, nil)
utils.HTTPError(w, "Method not allowed", http.StatusMethodNotAllowed, "HTTP001")
@ -201,23 +202,12 @@ func CreateServiceRoute(w http.ResponseWriter, req *http.Request) {
}
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!")
}
func CreateService(serviceRequest DockerServiceCreateRequest, OnLog func(string)) error {
utils.ConfigLock.Lock()
defer utils.ConfigLock.Unlock()
utils.Log("Starting creation of new service...")
fmt.Fprintf(w, "Starting creation of new service...\n")
flusher.Flush()
OnLog("Starting creation of new service...\n")
config := utils.ReadConfigFromFile()
configRoutes := config.HTTPConfig.ProxyConfig.Routes
@ -228,20 +218,17 @@ func CreateService(w http.ResponseWriter, req *http.Request, serviceRequest Dock
// check if services have the cosmos-force-network-secured label
for serviceName, service := range serviceRequest.Services {
utils.Log(fmt.Sprintf("Checking service %s...", serviceName))
fmt.Fprintf(w, "Checking service %s...\n", serviceName)
flusher.Flush()
OnLog(fmt.Sprintf("Checking service %s...\n", serviceName))
if service.Labels["cosmos-force-network-secured"] == "true" {
utils.Log(fmt.Sprintf("Forcing secure %s...", serviceName))
fmt.Fprintf(w, "Forcing secure %s...\n", serviceName)
flusher.Flush()
OnLog(fmt.Sprintf("Forcing secure %s...\n", serviceName))
newNetwork, errNC := CreateCosmosNetwork()
if errNC != nil {
utils.Error("CreateService: Network", err)
fmt.Fprintf(w, "[ERROR] Network %s cant be created\n", newNetwork)
flusher.Flush()
Rollback(rollbackActions, w, flusher)
OnLog(fmt.Sprintf("[ERROR] Network %s cant be created\n", newNetwork))
Rollback(rollbackActions, OnLog)
return err
}
@ -262,24 +249,21 @@ func CreateService(w http.ResponseWriter, req *http.Request, serviceRequest Dock
})
utils.Log(fmt.Sprintf("Created secure network %s", newNetwork))
fmt.Fprintf(w, "Created secure network %s\n", newNetwork)
flusher.Flush()
OnLog(fmt.Sprintf("Created secure network %s\n", newNetwork))
}
}
// Create networks
for networkToCreateName, networkToCreate := range serviceRequest.Networks {
utils.Log(fmt.Sprintf("Creating network %s...", networkToCreateName))
fmt.Fprintf(w, "Creating network %s...\n", networkToCreateName)
flusher.Flush()
OnLog(fmt.Sprintf("Creating network %s...\n", networkToCreateName))
// check if network already exists
_, err = DockerClient.NetworkInspect(DockerContext, networkToCreateName, doctype.NetworkInspectOptions{})
if err == nil {
utils.Error("CreateService: Network", err)
fmt.Fprintf(w, "[ERROR] Network %s already exists\n", networkToCreateName)
flusher.Flush()
Rollback(rollbackActions, w, flusher)
OnLog(fmt.Sprintf("[ERROR] Network %s already exists\n", networkToCreateName))
Rollback(rollbackActions, OnLog)
return err
}
@ -305,9 +289,8 @@ func CreateService(w http.ResponseWriter, req *http.Request, serviceRequest Dock
if err != nil {
utils.Error("CreateService: Rolling back changes because of -- Network", err)
fmt.Fprintf(w, "[ERROR] Rolling back changes because of -- Network creation error: "+err.Error())
flusher.Flush()
Rollback(rollbackActions, w, flusher)
OnLog(fmt.Sprintf("[ERROR] Rolling back changes because of -- Network creation error: %s\n", err.Error()))
Rollback(rollbackActions, OnLog)
return err
}
@ -319,15 +302,13 @@ func CreateService(w http.ResponseWriter, req *http.Request, serviceRequest Dock
// Write a response to the client
utils.Log(fmt.Sprintf("Network %s created", networkToCreateName))
fmt.Fprintf(w, "Network %s created\n", networkToCreateName)
flusher.Flush()
OnLog(fmt.Sprintf("Network %s created\n", networkToCreateName))
}
// Create volumes
for _, volume := range serviceRequest.Volumes {
utils.Log(fmt.Sprintf("Creating volume %s...", volume.Name))
fmt.Fprintf(w, "Creating volume %s...\n", volume.Name)
flusher.Flush()
OnLog(fmt.Sprintf("Creating volume %s...\n", volume.Name))
_, err = DockerClient.VolumeCreate(DockerContext, volumetype.CreateOptions{
Driver: volume.Driver,
@ -336,9 +317,8 @@ func CreateService(w http.ResponseWriter, req *http.Request, serviceRequest Dock
if err != nil {
utils.Error("CreateService: Rolling back changes because of -- Volume", err)
fmt.Fprintf(w, "[ERROR] Rolling back changes because of -- Volume creation error: "+err.Error())
flusher.Flush()
Rollback(rollbackActions, w, flusher)
OnLog(fmt.Sprintf("[ERROR] Rolling back changes because of -- Volume creation error: %s\n", err.Error()))
Rollback(rollbackActions, OnLog)
return err
}
@ -350,23 +330,20 @@ func CreateService(w http.ResponseWriter, req *http.Request, serviceRequest Dock
// Write a response to the client
utils.Log(fmt.Sprintf("Volume %s created", volume.Name))
fmt.Fprintf(w, "Volume %s created\n", volume.Name)
flusher.Flush()
OnLog(fmt.Sprintf("Volume %s created\n", volume.Name))
}
// pull images
for _, container := range serviceRequest.Services {
// Write a response to the client
utils.Log(fmt.Sprintf("Pulling image %s", container.Image))
fmt.Fprintf(w, "Pulling image %s\n", container.Image)
flusher.Flush()
OnLog(fmt.Sprintf("Pulling image %s\n", container.Image))
out, err := DockerClient.ImagePull(DockerContext, container.Image, doctype.ImagePullOptions{})
if err != nil {
utils.Error("CreateService: Rolling back changes because of -- Image pull", err)
fmt.Fprintf(w, "[ERROR] Rolling back changes because of -- Image pull error: "+err.Error())
flusher.Flush()
Rollback(rollbackActions, w, flusher)
OnLog(fmt.Sprintf("[ERROR] Rolling back changes because of -- Image pull error: %s\n", err.Error()))
Rollback(rollbackActions, OnLog)
return err
}
defer out.Close()
@ -374,21 +351,18 @@ func CreateService(w http.ResponseWriter, req *http.Request, serviceRequest Dock
// wait for image pull to finish
scanner := bufio.NewScanner(out)
for scanner.Scan() {
fmt.Fprintf(w, "%s\n", scanner.Text())
flusher.Flush()
OnLog(fmt.Sprintf("%s\n", scanner.Text()))
}
// Write a response to the client
utils.Log(fmt.Sprintf("Image %s pulled", container.Image))
fmt.Fprintf(w, "Image %s pulled\n", container.Image)
flusher.Flush()
OnLog(fmt.Sprintf("Image %s pulled\n", container.Image))
}
// Create containers
for _, container := range serviceRequest.Services {
utils.Log(fmt.Sprintf("Creating container %s...", container.Name))
fmt.Fprintf(w, "Creating container %s...\n", container.Name)
flusher.Flush()
OnLog(fmt.Sprintf("Creating container %s...\n", container.Name))
containerConfig := &conttype.Config{
Image: container.Image,
@ -443,29 +417,25 @@ func CreateService(w http.ResponseWriter, req *http.Request, serviceRequest Dock
if os.Getenv("HOSTNAME") != "" {
if _, err := os.Stat("/mnt/host"); os.IsNotExist(err) {
utils.Error("CreateService: Unable to create directory for bind mount in the host directory. Please mount the host / in Cosmos with -v /:/mnt/host to enable folder creations, or create the bind folder yourself", err)
fmt.Fprintf(w, "[ERROR] Unable to create directory for bind mount in the host directory. Please mount the host / in Cosmos with -v /:/mnt/host to enable folder creations, or create the bind folder yourself: "+err.Error())
flusher.Flush()
Rollback(rollbackActions, w, flusher)
OnLog(fmt.Sprintf("[ERROR] Unable to create directory for bind mount in the host directory. Please mount the host / in Cosmos with -v /:/mnt/host to enable folder creations, or create the bind folder yourself: %s\n", err.Error()))
Rollback(rollbackActions, OnLog)
return err
}
newSource = "/mnt/host" + newSource
}
utils.Log(fmt.Sprintf("Checking directory %s for bind mount", newSource))
fmt.Fprintf(w, "Checking directory %s for bind mount\n", newSource)
flusher.Flush()
OnLog(fmt.Sprintf("Checking directory %s for bind mount\n", newSource))
if _, err := os.Stat(newSource); os.IsNotExist(err) {
utils.Log(fmt.Sprintf("Not found. Creating directory %s for bind mount", newSource))
fmt.Fprintf(w, "Not found. Creating directory %s for bind mount\n", newSource)
flusher.Flush()
OnLog(fmt.Sprintf("Not found. Creating directory %s for bind mount\n", newSource))
err := os.MkdirAll(newSource, 0755)
if err != nil {
utils.Error("CreateService: Unable to create directory for bind mount. Make sure parent directories exist, and that Cosmos has permissions to create directories in the host directory", err)
fmt.Fprintf(w, "[ERROR] Unable to create directory for bind mount. Make sure parent directories exist, and that Cosmos has permissions to create directories in the host directory for bind mount: "+err.Error())
flusher.Flush()
Rollback(rollbackActions, w, flusher)
OnLog(fmt.Sprintf("[ERROR] Unable to create directory for bind mount. Make sure parent directories exist, and that Cosmos has permissions to create directories in the host directory: %s\n", err.Error()))
Rollback(rollbackActions, OnLog)
return err
}
@ -474,16 +444,14 @@ func CreateService(w http.ResponseWriter, req *http.Request, serviceRequest Dock
userInfo, err := user.Lookup(container.User)
if err != nil {
utils.Error("CreateService: Unable to lookup user", err)
fmt.Fprintf(w, "[ERROR] Unable to lookup user " + container.User + "." +err.Error())
flusher.Flush()
OnLog(fmt.Sprintf("[ERROR] Unable to lookup user " + container.User + "." +err.Error()))
} else {
uid, _ := strconv.Atoi(userInfo.Uid)
gid, _ := strconv.Atoi(userInfo.Gid)
err = os.Chown(newSource, uid, gid)
if err != nil {
utils.Error("CreateService: Unable to change ownership of directory", err)
fmt.Fprintf(w, "[ERROR] Unable to change ownership of directory: "+err.Error())
flusher.Flush()
OnLog(fmt.Sprintf("[ERROR] Unable to change ownership of directory: " + err.Error()))
}
}
}
@ -553,9 +521,8 @@ func CreateService(w http.ResponseWriter, req *http.Request, serviceRequest Dock
if err != nil {
utils.Error("CreateService: Rolling back changes because of -- Container", err)
fmt.Fprintf(w, "[ERROR] Rolling back changes because of -- Container creation error: "+err.Error())
flusher.Flush()
Rollback(rollbackActions, w, flusher)
OnLog(fmt.Sprintf("[ERROR] Rolling back changes because of -- Container creation error: "+err.Error()))
Rollback(rollbackActions, OnLog)
return err
}
@ -579,26 +546,23 @@ func CreateService(w http.ResponseWriter, req *http.Request, serviceRequest Dock
configRoutes = append([]utils.ProxyRouteConfig{(utils.ProxyRouteConfig)(route)}, configRoutes...)
} else {
utils.Error("CreateService: Rolling back changes because of -- Route already exist", nil)
fmt.Fprintf(w, "[ERROR] Rolling back changes because of -- Route already exist")
flusher.Flush()
Rollback(rollbackActions, w, flusher)
OnLog(fmt.Sprintf("[ERROR] Rolling back changes because of -- Route already exist"))
Rollback(rollbackActions, OnLog)
return errors.New("Route already exist")
}
}
// Write a response to the client
utils.Log(fmt.Sprintf("Container %s created", container.Name))
fmt.Fprintf(w, "Container %s created\n", container.Name)
flusher.Flush()
OnLog(fmt.Sprintf("Container %s created", container.Name))
}
// re-order containers dpeneding on depends_on
startOrder, err := ReOrderServices(serviceRequest.Services)
if err != nil {
utils.Error("CreateService: Rolling back changes because of -- Container", err)
fmt.Fprintf(w, "[ERROR] Rolling back changes because of -- Container creation error: "+err.Error())
flusher.Flush()
Rollback(rollbackActions, w, flusher)
OnLog(fmt.Sprintf("[ERROR] Rolling back changes because of -- Container creation error: "+err.Error()))
Rollback(rollbackActions, OnLog)
return err
}
@ -607,16 +571,14 @@ func CreateService(w http.ResponseWriter, req *http.Request, serviceRequest Dock
err = DockerClient.ContainerStart(DockerContext, container.Name, doctype.ContainerStartOptions{})
if err != nil {
utils.Error("CreateService: Start Container", err)
fmt.Fprintf(w, "[ERROR] Rolling back changes because of -- Container start error: "+err.Error())
flusher.Flush()
Rollback(rollbackActions, w, flusher)
OnLog(fmt.Sprintf("[ERROR] Rolling back changes because of -- Container start error: "+err.Error()))
Rollback(rollbackActions, OnLog)
return err
}
// Write a response to the client
utils.Log(fmt.Sprintf("Container %s started", container.Name))
fmt.Fprintf(w, "Container %s started\n", container.Name)
flusher.Flush()
OnLog(fmt.Sprintf("Container %s started", container.Name))
}
// Save the route configs
@ -626,8 +588,7 @@ func CreateService(w http.ResponseWriter, req *http.Request, serviceRequest Dock
// After all operations
utils.Log("CreateService: Operation succeeded. SERVICE STARTED")
fmt.Fprintf(w, "[OPERATION SUCCEEDED]. SERVICE STARTED\n")
flusher.Flush()
OnLog("[OPERATION SUCCEEDED]. SERVICE STARTED\n")
return nil
}

View file

@ -31,7 +31,7 @@ func ManageContainerRoute(w http.ResponseWriter, req *http.Request) {
// stop, start, restart, kill, remove, pause, unpause, recreate
action := utils.Sanitize(vars["action"])
if os.Getenv("HOSTNAME") != "" && containerName == os.Getenv("HOSTNAME") {
if os.Getenv("HOSTNAME") != "" && containerName == os.Getenv("HOSTNAME") && action != "update" && action != "recreate" {
utils.Error("ManageContainer - Container cannot update itself", nil)
utils.HTTPError(w, "Container cannot update itself", http.StatusBadRequest, "DS003")
return
@ -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, false)
_, err = RecreateContainer(container.Name, container)
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, false)
_, err = RecreateContainer(container.Name, container)
if err != nil {
utils.Error("Container Update - EditContainer", err)

View file

@ -10,8 +10,10 @@ import (
"fmt"
"strings"
"strconv"
"runtime"
"github.com/azukaar/cosmos-server/src/utils"
"github.com/docker/docker/client"
// natting "github.com/docker/go-connections/nat"
"github.com/docker/docker/api/types/container"
@ -86,6 +88,19 @@ func Connect() error {
return nil
}
func RecreateContainer(containerID string, containerConfig types.ContainerJSON) (string, error) {
if os.Getenv("HOSTNAME") != "" && os.Getenv("HOSTNAME") == containerID[1:] {
err := SelfRecreate()
if err != nil {
return "", err
}
} else {
return EditContainer(containerID, containerConfig, false)
}
return "", nil
}
func EditContainer(oldContainerID string, newConfig types.ContainerJSON, noLock bool) (string, error) {
if(oldContainerID != "" && !noLock) {
// no need to re-lock if we are reverting
@ -424,6 +439,19 @@ func Test() error {
return nil
}
func HasAutoUpdateOn(containerConfig types.ContainerJSON) bool {
if containerConfig.Config.Labels["cosmos-auto-update"] == "true" {
return true
}
config := utils.ReadConfigFromFile()
if os.Getenv("HOSTNAME") == containerConfig.Name[1:] && config.AutoUpdate {
return true
}
return false
}
func CheckUpdatesAvailable() map[string]bool {
result := make(map[string]bool)
@ -468,7 +496,7 @@ func CheckUpdatesAvailable() map[string]bool {
utils.Log("Updates available for " + container.Image)
result[container.Names[0]] = true
if !IsLabel(fullContainer, "cosmos-auto-update") {
if !HasAutoUpdateOn(fullContainer) {
rc.Close()
break
} else {
@ -477,7 +505,7 @@ func CheckUpdatesAvailable() map[string]bool {
} else if strings.Contains(newStr, "\"status\":\"Status: Image is up to date") {
utils.Log("No updates available for " + container.Image)
if !IsLabel(fullContainer, "cosmos-auto-update") {
if !HasAutoUpdateOn(fullContainer) {
rc.Close()
break
}
@ -505,9 +533,9 @@ func CheckUpdatesAvailable() map[string]bool {
}
}
if needsUpdate && IsLabel(fullContainer, "cosmos-auto-update") {
if needsUpdate && HasAutoUpdateOn(fullContainer) {
utils.Log("Downlaoded new update for " + container.Image + " ready to install")
_, err := EditContainer(container.ID, fullContainer, false)
_, err := RecreateContainer(container.Names[0], fullContainer)
if err != nil {
utils.Error("CheckUpdatesAvailable - Failed to update - ", err)
} else {
@ -518,3 +546,82 @@ func CheckUpdatesAvailable() map[string]bool {
return result
}
func RemoveSelfUpdater() error {
utils.Log("Checking for self updater agent")
// look for a container with the name cosmos-self-updater-agent
containers, err := ListContainers()
if err != nil {
utils.Error("RemoveSelfUpdater", err)
return err
}
for _, container := range containers {
if container.Names[0] == "/cosmos-self-updater-agent" {
utils.Log("Found. Removing self updater agent")
err := DockerClient.ContainerKill(DockerContext, container.ID, "SIGKILL")
if err != nil {
utils.Error("RemoveSelfUpdater", err)
}
err = DockerClient.ContainerRemove(DockerContext, container.ID, types.ContainerRemoveOptions{
Force: true,
})
if err != nil {
utils.Error("RemoveSelfUpdater", err)
return err
}
}
}
return nil
}
func SelfRecreate() error {
if os.Getenv("HOSTNAME") == "" {
utils.Error("SelfRecreate - not using Docker", nil)
return errors.New("SelfRecreate - not using Docker")
}
// make sure to remove resiude of old self updater
RemoveSelfUpdater()
containerName := os.Getenv("HOSTNAME")
version := "latest"
// if arm
if runtime.GOARCH == "arm" {
version = "latest-arm64"
}
service := DockerServiceCreateRequest{
Services: map[string]ContainerCreateRequestContainer {},
}
service.Services["cosmos-self-updater-agent"] = ContainerCreateRequestContainer{
Name: "cosmos-self-updater-agent",
Image: "azukaar/docker-self-updater:" + version,
RestartPolicy: "no",
Environment: []string{
"CONTAINER_NAME=" + containerName,
"ACTION=recreate",
"DOCKER_HOST=" + os.Getenv("DOCKER_HOST"),
},
Volumes: []mountType.Mount{
{
Type: mountType.TypeBind,
Source: "/var/run/docker.sock",
Target: "/var/run/docker.sock",
},
},
};
err := CreateService(service, func (msg string) {})
if err != nil {
return err
}
return nil
}

View file

@ -29,8 +29,13 @@ func NewDB(w http.ResponseWriter, req *http.Request) (string, error) {
imageName := "mongo:latest"
//if ARM use amd64/mongo
if runtime.GOARCH == "arm64" {
utils.Warn("ARM64 detected. Using ARM mongo 4.4")
imageName = "amd64/mongo:4.4"
// if CPU is missing AVX, use 4.4
if runtime.GOARCH == "amd64" && !cpu.X86.HasAVX {
} else if runtime.GOARCH == "amd64" && !cpu.X86.HasAVX {
utils.Warn("CPU does not support AVX. Using mongo 4.4")
imageName = "mongo:4.4"
}

View file

@ -24,6 +24,8 @@ func main() {
docker.BootstrapAllContainersFromTags()
docker.RemoveSelfUpdater()
version, err := docker.DockerClient.ServerVersion(context.Background())
if err == nil {
utils.Log("Docker API version: " + version.APIVersion)

View file

@ -15,10 +15,6 @@ export default defineConfig({
secure: false,
ws: true,
}
},
watch: {
usePolling: true
}
}
})