diff --git a/.circleci/config.yml b/.circleci/config.yml
index ec169e7..b62ef48 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -11,38 +11,7 @@ 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
@@ -70,38 +39,7 @@ 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
@@ -109,10 +47,6 @@ jobs:
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
diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 0000000..ac793ca
--- /dev/null
+++ b/.dockerignore
@@ -0,0 +1,3 @@
+node_modules
+static
+build
\ No newline at end of file
diff --git a/client/src/pages/servapps/actionBar.jsx b/client/src/pages/servapps/actionBar.jsx
index bd0313d..3de70e1 100644
--- a/client/src/pages/servapps/actionBar.jsx
+++ b/client/src/pages/servapps/actionBar.jsx
@@ -39,12 +39,19 @@ const GetActions = ({
let actions = [
{
- t: 'Update Available',
+ t: 'Update Available, Click to Update',
if: ['update_available'],
e: {doTo('update')}} size={isMiniMobile ? 'medium' : 'large'}>
},
+ {
+ t: 'No Update Available. Click to Force Pull',
+ if: ['update_not_available'],
+ e: {doTo('update')}} size={isMiniMobile ? 'medium' : 'large'}>
+
+
+ },
{
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 {action.e}
})}
diff --git a/client/src/pages/servapps/containers/index.jsx b/client/src/pages/servapps/containers/index.jsx
index 6733d91..117f9a2 100644
--- a/client/src/pages/servapps/containers/index.jsx
+++ b/client/src/pages/servapps/containers/index.jsx
@@ -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:
+ children:
},
{
title: 'Logs',
diff --git a/client/src/pages/servapps/containers/overview.jsx b/client/src/pages/servapps/containers/overview.jsx
index f144211..b85d2e4 100644
--- a/client/src/pages/servapps/containers/overview.jsx
+++ b/client/src/pages/servapps/containers/overview.jsx
@@ -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]}
/>
{containerInfo.State.Status !== 'running' && (
@@ -133,7 +134,8 @@ const ContainerOverview = ({ containerInfo, config, refresh }) => {
{
setIsUpdating(true);
diff --git a/client/src/pages/servapps/servapps.jsx b/client/src/pages/servapps/servapps.jsx
index a29ff34..d895c9b 100644
--- a/client/src/pages/servapps/servapps.jsx
+++ b/client/src/pages/servapps/servapps.jsx
@@ -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 = () => {
-
+
{
{
const name = app.Names[0].replace('/', '');
diff --git a/docker.arm64.sh b/docker.arm64.sh
index 153f6d3..febc565 100644
--- a/docker.arm64.sh
+++ b/docker.arm64.sh
@@ -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 \
diff --git a/docker.sh b/docker.sh
index f2f7901..2050bb1 100644
--- a/docker.sh
+++ b/docker.sh
@@ -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 \
diff --git a/dockerfile b/dockerfile
index c0f5025..958a69c 100644
--- a/dockerfile
+++ b/dockerfile
@@ -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"]
diff --git a/dockerfile.arm64 b/dockerfile.arm64
index 9398bdc..5a6e1a0 100644
--- a/dockerfile.arm64
+++ b/dockerfile.arm64
@@ -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"]
diff --git a/package.json b/package.json
index 69e9293..2be8a70 100644
--- a/package.json
+++ b/package.json
@@ -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"
},
diff --git a/src/configapi/get.go b/src/configapi/get.go
index 69c7cef..fac06ea 100644
--- a/src/configapi/get.go
+++ b/src/configapi/get.go
@@ -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)
diff --git a/src/docker/api_autoupdate.go b/src/docker/api_autoupdate.go
index db81e7a..49654af 100644
--- a/src/docker/api_autoupdate.go
+++ b/src/docker/api_autoupdate.go
@@ -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
}
diff --git a/src/docker/api_blueprint.go b/src/docker/api_blueprint.go
index e434693..5017f76 100644
--- a/src/docker/api_blueprint.go
+++ b/src/docker/api_blueprint.go
@@ -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
}
diff --git a/src/docker/api_managecont.go b/src/docker/api_managecont.go
index 40e5000..4c8c36a 100644
--- a/src/docker/api_managecont.go
+++ b/src/docker/api_managecont.go
@@ -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)
diff --git a/src/docker/docker.go b/src/docker/docker.go
index c80e171..8e3405f 100644
--- a/src/docker/docker.go
+++ b/src/docker/docker.go
@@ -10,7 +10,9 @@ import (
"fmt"
"strings"
"strconv"
+ "runtime"
"github.com/azukaar/cosmos-server/src/utils"
+
"github.com/docker/docker/client"
// natting "github.com/docker/go-connections/nat"
@@ -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
+}
\ No newline at end of file
diff --git a/src/docker/run.go b/src/docker/run.go
index d741974..7fab935 100644
--- a/src/docker/run.go
+++ b/src/docker/run.go
@@ -28,13 +28,18 @@ func NewDB(w http.ResponseWriter, req *http.Request) (string, error) {
monHost := "cosmos-mongo-" + id
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"
}
-
+
err := RunContainer(
imageName,
monHost,
diff --git a/src/index.go b/src/index.go
index 758a212..c8c3542 100644
--- a/src/index.go
+++ b/src/index.go
@@ -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)
diff --git a/vite.config.js b/vite.config.js
index da5d2a2..c697baa 100644
--- a/vite.config.js
+++ b/vite.config.js
@@ -15,10 +15,6 @@ export default defineConfig({
secure: false,
ws: true,
}
- },
-
- watch: {
- usePolling: true
}
}
})