diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index d3951eb..a5e343f 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -8,7 +8,7 @@ on:
branches: ["main"]
env:
- VERSION: "1.5.7"
+ VERSION: "1.5.8"
jobs:
docker:
diff --git a/cmd/server/main.go b/cmd/server/main.go
index 51330e7..a67e589 100644
--- a/cmd/server/main.go
+++ b/cmd/server/main.go
@@ -12,6 +12,7 @@ func main() {
os.Getenv("DATA_PATH"),
os.Getenv("LOG_LEVEL"),
os.Getenv("SSL_ENABLED"),
+ os.Getenv("STALENESS_CHECK"),
)
s.Run(os.Getenv("BIND_ADDRESS"))
}
\ No newline at end of file
diff --git a/pkg/agent/agent.go b/pkg/agent/agent.go
index 43d9802..5deb70a 100644
--- a/pkg/agent/agent.go
+++ b/pkg/agent/agent.go
@@ -21,12 +21,15 @@ var (
logLevel string
wsUrl string
token string
+ stalenessCheck string = "ON"
)
func Main() {
parseArgs()
setLogLevel(logLevel)
- go dockerapi.ContainerScheduleRefreshStaleStatus()
+ if stalenessCheck != "OFF" {
+ go dockerapi.ContainerScheduleRefreshStaleStatus()
+ }
listen()
}
@@ -34,6 +37,7 @@ func parseArgs() {
logLevel = os.Getenv("LOG_LEVEL")
serverUrl := os.Getenv("SERVER_URL")
token = os.Getenv("TOKEN")
+ stalenessCheck = os.Getenv("STALENESS_CHECK")
serverScheme := "ws"
if strings.HasPrefix(serverUrl, "https") {
diff --git a/pkg/common/common.go b/pkg/common/common.go
index 8bd56fb..7987f4d 100644
--- a/pkg/common/common.go
+++ b/pkg/common/common.go
@@ -1,3 +1,3 @@
package common
-const Version = "1.5.7"
\ No newline at end of file
+const Version = "1.5.8"
\ No newline at end of file
diff --git a/pkg/dockerapi/compose.go b/pkg/dockerapi/compose.go
index b544678..2efb5ce 100644
--- a/pkg/dockerapi/compose.go
+++ b/pkg/dockerapi/compose.go
@@ -195,27 +195,43 @@ func ComposeLogs(req *DockerComposeLogs, ws *websocket.Conn) error {
return nil
}
-func createTempComposeFile(projectName string, definition string) (string, string, error) {
+func createTempComposeFile(projectName string, definition string, variables map[string]store.VariableValue) (string, string, string, error) {
dir, err := os.MkdirTemp("", projectName)
if err != nil {
log.Error().Err(err).Msg("Error while creating temp directory for compose")
- return "", "", err
+ return "", "", "", err
}
- filename := filepath.Join(dir, "compose.yaml")
- composeFile, err := os.Create(filename)
+ composeFilename := filepath.Join(dir, "compose.yaml")
+ composeFile, err := os.Create(composeFilename)
if err != nil {
log.Error().Err(err).Msg("Error while creating temp compose file")
- return "", "", err
+ return "", "", "", err
}
_ , err = composeFile.WriteString(definition)
if err != nil {
log.Error().Err(err).Msg("Error while writing to temp compose file")
- return "", "", err
+ return "", "", "", err
}
- return dir, filename, nil
+ envFilename := filepath.Join(dir, ".env")
+ envFile, err := os.Create(envFilename)
+ if err != nil {
+ log.Error().Err(err).Msg("Error while creating temp compose file")
+ return "", "", "", err
+ }
+
+ envVars := toEnvFormat(variables)
+ for _, v := range envVars {
+ _ , err = envFile.WriteString(v + "\r\n")
+ if err != nil {
+ log.Error().Err(err).Msg("Error while writing to temp .env file")
+ return "", "", "", err
+ }
+ }
+
+ return dir, composeFilename, envFilename, nil
}
func toEnvFormat(variables map[string]store.VariableValue) ([]string) {
@@ -230,8 +246,7 @@ func toEnvFormat(variables map[string]store.VariableValue) ([]string) {
return ret
}
-func processVars(cmd *exec.Cmd, variables map[string]store.VariableValue, ws *websocket.Conn, print bool) {
- cmd.Env = os.Environ()
+func logVars(cmd *exec.Cmd, variables map[string]store.VariableValue, ws *websocket.Conn, print bool) {
if print {
ws.WriteMessage(websocket.TextMessage, []byte("*** SETTING BELOW VARIABLES: ***\n\n"))
}
@@ -251,35 +266,31 @@ func processVars(cmd *exec.Cmd, variables map[string]store.VariableValue, ws *we
ws.WriteMessage(websocket.TextMessage, []byte(fmt.Sprintf("%s=%s\n", k, val)))
}
}
-
- for _, v := range toEnvFormat(variables) {
- cmd.Env = append(cmd.Env, v)
- }
}
func performComposeAction(action string, projectName string, definition string, variables map[string]store.VariableValue, ws *websocket.Conn, printVars bool) error {
- dir, file, err := createTempComposeFile(projectName, definition)
- log.Debug().Str("fileName", file).Msg("Created temporary compose file")
+ dir, composefile, envfile, err := createTempComposeFile(projectName, definition, variables)
+ log.Debug().Str("composeFileName", composefile).Str("envFileName", envfile).Msg("Created temporary compose file and .env file")
if err != nil {
return err
}
defer func() {
- log.Debug().Str("fileName", file).Msg("Deleting temporary compose file")
+ log.Debug().Str("fileName", composefile).Msg("Deleting temporary compose file and .env file")
os.RemoveAll(dir)
}()
var cmd *exec.Cmd
switch action {
case "up":
- cmd = exec.Command("docker-compose", "-p", projectName, "-f", file, action, "-d")
+ cmd = exec.Command("docker-compose", "-p", projectName, "--env-file", envfile, "-f", composefile, action, "-d")
case "down":
- cmd = exec.Command("docker-compose", "-p", projectName, action)
+ cmd = exec.Command("docker-compose", "-p", projectName, "--env-file", envfile, action)
case "pull":
- cmd = exec.Command("docker-compose", "-p", projectName, "-f", file, action)
+ cmd = exec.Command("docker-compose", "-p", projectName, "--env-file", envfile, "-f", composefile, action)
default:
panic(fmt.Errorf("unknown compose action %s", action))
}
- processVars(cmd, variables, ws, printVars)
+ logVars(cmd, variables, ws, printVars)
ws.WriteMessage(websocket.TextMessage, []byte(fmt.Sprintf("\n*** STARTING ACTION: %s ***\n\n", action)))
f, err := pty.Start(cmd)
diff --git a/pkg/dockerapi/container_stale_check.go b/pkg/dockerapi/container_stale_check.go
index 6fa248e..9e9fc57 100644
--- a/pkg/dockerapi/container_stale_check.go
+++ b/pkg/dockerapi/container_stale_check.go
@@ -43,7 +43,7 @@ func ContainerScheduleRefreshStaleStatus() {
for {
log.Info().Msg("Refreshing container stale status")
ContainerRefreshStaleStatus()
- time.Sleep(1 * time.Hour)
+ time.Sleep(24 * time.Hour)
}
}
diff --git a/pkg/dockerapi/image.go b/pkg/dockerapi/image.go
index 86d0d54..f0d4131 100644
--- a/pkg/dockerapi/image.go
+++ b/pkg/dockerapi/image.go
@@ -18,7 +18,7 @@ func ImageList(req *DockerImageList) (*DockerImageListResponse, error) {
return nil, err
}
- dcontainers, err := cli.ContainerList(context.Background(), types.ContainerListOptions{All: req.All})
+ dcontainers, err := cli.ContainerList(context.Background(), types.ContainerListOptions{All: true})
if err != nil {
return nil, err
}
diff --git a/pkg/dockerapi/models.go b/pkg/dockerapi/models.go
index 82af2d6..95e5945 100644
--- a/pkg/dockerapi/models.go
+++ b/pkg/dockerapi/models.go
@@ -102,8 +102,9 @@ type DockerVolumeList struct {
}
type Volume struct {
- Driver string `json:"driver"`
- Name string `json:"name"`
+ Driver string `json:"driver"`
+ Name string `json:"name"`
+ InUse bool `json:"inUse"`
}
type DockerVolumeListResponse struct {
@@ -131,6 +132,7 @@ type Network struct {
Name string `json:"name"`
Driver string `json:"driver"`
Scope string `json:"scope"`
+ InUse bool `json:"inUse"`
}
type DockerNetworkListResponse struct {
diff --git a/pkg/dockerapi/network.go b/pkg/dockerapi/network.go
index c34d279..67fa3f7 100644
--- a/pkg/dockerapi/network.go
+++ b/pkg/dockerapi/network.go
@@ -15,6 +15,20 @@ func NetworkList(req *DockerNetworkList) (*DockerNetworkListResponse, error) {
return nil, err
}
+ dcontainers, err := cli.ContainerList(context.Background(), types.ContainerListOptions{All: true})
+ if err != nil {
+ return nil, err
+ }
+
+ usedNetworks := make(map[string]interface{}, 0)
+ for _, c := range dcontainers {
+ if c.NetworkSettings != nil {
+ for _, n := range c.NetworkSettings.Networks {
+ usedNetworks[n.NetworkID] = nil
+ }
+ }
+ }
+
dnetworks, err := cli.NetworkList(context.Background(), types.NetworkListOptions{})
if err != nil {
return nil, err
@@ -22,11 +36,13 @@ func NetworkList(req *DockerNetworkList) (*DockerNetworkListResponse, error) {
networks := make([]Network, len(dnetworks))
for i, item := range dnetworks {
+ _, inUse := usedNetworks[item.ID]
networks[i] = Network{
Id: item.ID,
Name: item.Name,
Driver: item.Driver,
Scope: item.Scope,
+ InUse: inUse,
}
}
diff --git a/pkg/dockerapi/volume.go b/pkg/dockerapi/volume.go
index 4f291bf..7cc14c8 100644
--- a/pkg/dockerapi/volume.go
+++ b/pkg/dockerapi/volume.go
@@ -4,7 +4,9 @@ import (
"context"
"sort"
+ "github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/filters"
+ "github.com/docker/docker/api/types/mount"
"github.com/docker/docker/api/types/volume"
"github.com/docker/docker/client"
)
@@ -15,6 +17,20 @@ func VolumeList(req *DockerVolumeList) (*DockerVolumeListResponse, error) {
return nil, err
}
+ dcontainers, err := cli.ContainerList(context.Background(), types.ContainerListOptions{All: true})
+ if err != nil {
+ return nil, err
+ }
+
+ usedVolumes := make(map[string]interface{}, 0)
+ for _, c := range dcontainers {
+ for _, m := range c.Mounts {
+ if m.Type == mount.TypeVolume {
+ usedVolumes[m.Name] = nil
+ }
+ }
+ }
+
dvolumes, err := cli.VolumeList(context.Background(), volume.ListOptions{})
if err != nil {
return nil, err
@@ -22,9 +38,11 @@ func VolumeList(req *DockerVolumeList) (*DockerVolumeListResponse, error) {
volumes := make([]Volume, len(dvolumes.Volumes))
for i, item := range dvolumes.Volumes {
+ _, inUse := usedVolumes[item.Name]
volumes[i] = Volume{
Driver: item.Driver,
Name: item.Name,
+ InUse: inUse,
}
}
@@ -56,7 +74,7 @@ func VolumesPrune(req *DockerVolumesPrune) (*DockerVolumesPruneResponse, error)
}
all := "true"
- if req.All {
+ if !req.All {
all = "false"
}
allFilter := filters.KeyValuePair{Key: "all", Value: all}
diff --git a/pkg/server/handler/request_docker.go b/pkg/server/handler/request_docker.go
index 28ce692..3273e4b 100644
--- a/pkg/server/handler/request_docker.go
+++ b/pkg/server/handler/request_docker.go
@@ -78,7 +78,7 @@ func (r *dockerContainerRemoveRequest) bind(c echo.Context, m *dockerapi.DockerC
type dockerImageRemoveRequest struct {
Id string `json:"id" validate:"required,max=100"`
- Force bool `json:"force" validate:"required"`
+ Force bool `json:"force"`
}
func (r *dockerImageRemoveRequest) bind(c echo.Context, m *dockerapi.DockerImageRemove) error {
diff --git a/pkg/server/server.go b/pkg/server/server.go
index 63ae934..2124d2b 100644
--- a/pkg/server/server.go
+++ b/pkg/server/server.go
@@ -35,7 +35,7 @@ type Server struct {
sslEnabled bool
}
-func NewServer(dbConnectionString string, dataPath string, logLevel string, sslEnabled string) (*Server) {
+func NewServer(dbConnectionString string, dataPath string, logLevel string, sslEnabled string, stalenessCheck string) (*Server) {
s := Server{}
setLogLevel(logLevel)
@@ -82,7 +82,9 @@ func NewServer(dbConnectionString string, dataPath string, logLevel string, sslE
log.Error().Err(err).Msg("Error while updating old version data")
}
- go dockerapi.ContainerScheduleRefreshStaleStatus()
+ if stalenessCheck != "OFF" {
+ go dockerapi.ContainerScheduleRefreshStaleStatus()
+ }
// Web Server
s.handler = h
diff --git a/web/src/app/images/image-list.tsx b/web/src/app/images/image-list.tsx
index 1232b27..5676ebf 100644
--- a/web/src/app/images/image-list.tsx
+++ b/web/src/app/images/image-list.tsx
@@ -52,7 +52,7 @@ export default function ImageList() {
{
method: "POST",
headers: { "Content-Type": "application/json" },
- body: JSON.stringify({ id: image?.id, force: true }),
+ body: JSON.stringify({ id: image?.id, force: false }),
}
)
if (!response.ok) {
@@ -162,12 +162,14 @@ export default function ImageList() {
{item.inUse ? "In use" : "Unused"}
{convertByteToMb(item.size)}
- {
- e.stopPropagation()
- handleDeleteImageConfirmation(item)
- }}
- />
+ {!item.inUse && (
+ {
+ e.stopPropagation()
+ handleDeleteImageConfirmation(item)
+ }}
+ />
+ )}
))}
diff --git a/web/src/app/networks/network-list.tsx b/web/src/app/networks/network-list.tsx
index d5fdeba..b2a123f 100644
--- a/web/src/app/networks/network-list.tsx
+++ b/web/src/app/networks/network-list.tsx
@@ -28,6 +28,15 @@ import { toastFailed, toastSuccess } from "@/lib/utils"
import apiBaseUrl from "@/lib/api-base-url"
import DeleteDialog from "@/components/delete-dialog"
+const systemNetwoks = [
+ "none",
+ "bridge",
+ "host",
+ "ingress",
+ "docker_gwbridge",
+ "docker_volumes-backup-extension-desktop-extension_default",
+]
+
export default function NetworkList() {
const { nodeId } = useParams()
const { nodeHead } = useNodeHead(nodeId!)
@@ -137,6 +146,7 @@ export default function NetworkList() {
Name
Driver
Scope
+ Status
Actions
@@ -151,13 +161,16 @@ export default function NetworkList() {
{item.name}
{item.driver}
{item.scope}
+ {item.inUse ? "In use" : "Unused"}
- {
- e.stopPropagation()
- handleDeleteNetworkConfirmation(item)
- }}
- />
+ {!systemNetwoks.includes(item.name) && !item.inUse && (
+ {
+ e.stopPropagation()
+ handleDeleteNetworkConfirmation(item)
+ }}
+ />
+ )}
))}
diff --git a/web/src/app/volumes/volume-list.tsx b/web/src/app/volumes/volume-list.tsx
index b675170..c9d09d0 100644
--- a/web/src/app/volumes/volume-list.tsx
+++ b/web/src/app/volumes/volume-list.tsx
@@ -139,6 +139,7 @@ export default function VolumeList() {
Driver
Name
+ Status
Actions
@@ -151,13 +152,16 @@ export default function VolumeList() {
{item.driver}
{item.name}
+ {item.inUse ? "In use" : "Unused"}
- {
- e.stopPropagation()
- handleDeleteVolumeConfirmation(item)
- }}
- />
+ {!item.inUse && (
+ {
+ e.stopPropagation()
+ handleDeleteVolumeConfirmation(item)
+ }}
+ />
+ )}
))}
diff --git a/web/src/lib/api-models.ts b/web/src/lib/api-models.ts
index 0025c90..0c4bfb8 100644
--- a/web/src/lib/api-models.ts
+++ b/web/src/lib/api-models.ts
@@ -106,6 +106,7 @@ export interface IImage {
export interface IVolume {
driver: string
name: string
+ inUse: boolean
}
export interface INetwork {
@@ -113,6 +114,7 @@ export interface INetwork {
name: string
driver: string
scope: string
+ inUse: boolean
}
export interface IComposeLibraryItemHead {
diff --git a/web/src/lib/version.ts b/web/src/lib/version.ts
index 0ff6f20..215c988 100644
--- a/web/src/lib/version.ts
+++ b/web/src/lib/version.ts
@@ -1 +1 @@
-export const VERSION = "1.5.7"
+export const VERSION = "1.5.8"