From b8761de020828829aae937e7d3ea432f1145b339 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gerg=C5=91=20Dulai?= Date: Tue, 3 Feb 2026 21:09:41 +0100 Subject: [PATCH 1/3] Node endpoints with container handling --- container/container.go | 1 + docker-compose.yml | 8 +++ go.mod | 4 +- go.sum | 4 ++ node/node.go | 107 +++++++++++++++++++++++++++++++++++++++++ router/router.go | 3 ++ 6 files changed, 125 insertions(+), 2 deletions(-) create mode 100644 container/container.go create mode 100644 docker-compose.yml create mode 100644 node/node.go diff --git a/container/container.go b/container/container.go new file mode 100644 index 0000000..92b9571 --- /dev/null +++ b/container/container.go @@ -0,0 +1 @@ +package container diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..666adb4 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,8 @@ +version: "3.9" +services: + simple-cluster-node: + build: . + env_file: + - .env + ports: + - "8080:8080" diff --git a/go.mod b/go.mod index 279fa64..0d16c0c 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.25.6 require ( github.com/google/uuid v1.6.0 github.com/gorilla/mux v1.8.1 - github.com/moby/moby/client v0.2.1 + github.com/moby/moby/client v0.2.2 gitlab.com/gdulai/simpleloglvl v0.0.0-20250930234204-a64d074990c1 ) @@ -21,7 +21,7 @@ require ( github.com/go-logr/stdr v1.2.2 // indirect github.com/joho/godotenv v1.5.1 github.com/moby/docker-image-spec v1.3.1 // indirect - github.com/moby/moby/api v1.52.0 // indirect + github.com/moby/moby/api v1.53.0 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.1.1 // indirect go.opentelemetry.io/auto/sdk v1.1.0 // indirect diff --git a/go.sum b/go.sum index 752e522..b47ce34 100644 --- a/go.sum +++ b/go.sum @@ -31,8 +31,12 @@ github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3N github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= github.com/moby/moby/api v1.52.0 h1:00BtlJY4MXkkt84WhUZPRqt5TvPbgig2FZvTbe3igYg= github.com/moby/moby/api v1.52.0/go.mod h1:8mb+ReTlisw4pS6BRzCMts5M49W5M7bKt1cJy/YbAqc= +github.com/moby/moby/api v1.53.0 h1:PihqG1ncw4W+8mZs69jlwGXdaYBeb5brF6BL7mPIS/w= +github.com/moby/moby/api v1.53.0/go.mod h1:8mb+ReTlisw4pS6BRzCMts5M49W5M7bKt1cJy/YbAqc= github.com/moby/moby/client v0.2.1 h1:1Grh1552mvv6i+sYOdY+xKKVTvzJegcVMhuXocyDz/k= github.com/moby/moby/client v0.2.1/go.mod h1:O+/tw5d4a1Ha/ZA/tPxIZJapJRUS6LNZ1wiVRxYHyUE= +github.com/moby/moby/client v0.2.2 h1:Pt4hRMCAIlyjL3cr8M5TrXCwKzguebPAc2do2ur7dEM= +github.com/moby/moby/client v0.2.2/go.mod h1:2EkIPVNCqR05CMIzL1mfA07t0HvVUUOl85pasRz/GmQ= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040= diff --git a/node/node.go b/node/node.go new file mode 100644 index 0000000..59aca7e --- /dev/null +++ b/node/node.go @@ -0,0 +1,107 @@ +package node + +import ( + "context" + "encoding/json" + "net/http" + + "github.com/moby/moby/api/types/container" + "github.com/moby/moby/client" + log "gitlab.com/gdulai/simpleloglvl" +) + +type BuildCommand struct { + Repository string `json:"repository"` + Branch string `json:"branch"` + Descriptor string `json:"descriptor"` +} + +func HandleBuild(w http.ResponseWriter, r *http.Request) { + +} + +func build(command BuildCommand) { + +} + +func HandleStart(w http.ResponseWriter, r *http.Request) { + +} + +func start() { + +} + +func HandleStop(w http.ResponseWriter, r *http.Request) { + decoder := json.NewDecoder(r.Body) + var containerId string + err := decoder.Decode(&containerId) + if err != nil { + log.LogError("Failed to decode node/stop request body!") + w.WriteHeader(http.StatusBadRequest) + return + } + if containerId == "" { + log.LogError("Container id mus be specified for node/stop!") + w.WriteHeader(http.StatusBadRequest) + return + } + + if stop(containerId) { + w.WriteHeader(http.StatusOK) + } else { + w.WriteHeader(http.StatusInternalServerError) + } +} + +func stop(containerId string) bool { + cli := openDockerClient() + _, err := cli.ContainerStop(context.Background(), containerId, client.ContainerStopOptions{}) + if err != nil { + log.LogError("Failed to stop container: %s\n%s", containerId, err) + return false + } + log.LogInfo("Successfully stopped container: %s", containerId) + return true +} + +func HandleInfo(w http.ResponseWriter, r *http.Request) { + json.NewEncoder(w).Encode(info()) +} + +func info() []container.Summary { + cli := openDockerClient() + // List all containers (running and stopped) + containers, err := cli.ContainerList(context.Background(), client.ContainerListOptions{ + All: true, + }) + if err != nil { + log.LogFatalError("Failed to list containers: %v", err) + } + + var summaries []container.Summary + // Print container info + for _, c := range containers.Items { + summaries = append(summaries, c) + name := "" + if len(c.Names) > 0 { + name = c.Names[0] + } + log.LogInfo("ID: %s Name: %s Image: %s Status: %s\n", + c.ID[:12], name, c.Image, c.Status) + } + + return summaries + +} + +func openDockerClient() *client.Client { + cli, err := client.New( + client.FromEnv, + client.WithAPIVersionFromEnv(), + ) + if err != nil { + log.LogFatalError("Failed to create Docker client: %v", err) + } + return cli +} diff --git a/router/router.go b/router/router.go index 3204d1f..a2c900d 100644 --- a/router/router.go +++ b/router/router.go @@ -5,6 +5,7 @@ import ( "net/http" "os" "simple-cluster-node/health" + "simple-cluster-node/node" "github.com/google/uuid" "github.com/gorilla/mux" @@ -14,7 +15,9 @@ import ( func SetupRouter() *mux.Router { r := mux.NewRouter() r.Use(identifyRequest, corsCheck, logRequest) + r.HandleFunc("/health", health.Handle).Methods("GET", "OPTIONS") + r.HandleFunc("/node/info", node.HandleInfo).Methods("GET", "OPTIONS") /* r.HandleFunc("/album", handler.GetAlbums).Methods("GET", "OPTIONS") -- 2.49.1 From 1369b3cf8b778cb8eedf40464d8837c0bbd111ad Mon Sep 17 00:00:00 2001 From: Gergo Dulai Date: Tue, 3 Feb 2026 22:03:08 +0100 Subject: [PATCH 2/3] Container start/stop impl --- node/node.go | 61 ++++++++++++++++++++++++++++++++++++------------ router/router.go | 10 ++------ 2 files changed, 48 insertions(+), 23 deletions(-) diff --git a/node/node.go b/node/node.go index 59aca7e..dba42df 100644 --- a/node/node.go +++ b/node/node.go @@ -25,44 +25,75 @@ func build(command BuildCommand) { } func HandleStart(w http.ResponseWriter, r *http.Request) { + requestId := r.Context().Value("requestId") -} - -func start() { - -} - -func HandleStop(w http.ResponseWriter, r *http.Request) { decoder := json.NewDecoder(r.Body) var containerId string err := decoder.Decode(&containerId) if err != nil { - log.LogError("Failed to decode node/stop request body!") + log.LogError("Failed to decode node/start request body! (%s)\n%s", requestId, err) w.WriteHeader(http.StatusBadRequest) return } if containerId == "" { - log.LogError("Container id mus be specified for node/stop!") + log.LogError("Container id mus be specified for node/start! (%s)", requestId) w.WriteHeader(http.StatusBadRequest) return } - if stop(containerId) { + err = start(containerId) + if err == nil { + log.LogInfo("Successfully container start: %s (%s)", containerId, requestId) w.WriteHeader(http.StatusOK) } else { + log.LogInfo("Failed container start! (%s)\n%s", requestId, err) w.WriteHeader(http.StatusInternalServerError) } } -func stop(containerId string) bool { +func start(containerId string) error { + cli := openDockerClient() + _, err := cli.ContainerStart(context.Background(), containerId, client.ContainerStartOptions{}) + if err != nil { + return err + } + return nil +} + +func HandleStop(w http.ResponseWriter, r *http.Request) { + requestId := r.Context().Value("requestId") + + decoder := json.NewDecoder(r.Body) + var containerId string + err := decoder.Decode(&containerId) + if err != nil { + log.LogError("Failed to decode node/stop request body! (%s)", requestId) + w.WriteHeader(http.StatusBadRequest) + return + } + if containerId == "" { + log.LogError("Container id must be specified for node/stop! (%s)", requestId) + w.WriteHeader(http.StatusBadRequest) + return + } + + err = stop(containerId) + if err == nil { + log.LogInfo("Successfully container stop: %s (%s)", containerId, requestId) + w.WriteHeader(http.StatusOK) + } else { + log.LogInfo("Failed container stop! (%s)\n%s", requestId, err) + w.WriteHeader(http.StatusInternalServerError) + } +} + +func stop(containerId string) error { cli := openDockerClient() _, err := cli.ContainerStop(context.Background(), containerId, client.ContainerStopOptions{}) if err != nil { - log.LogError("Failed to stop container: %s\n%s", containerId, err) - return false + return err } - log.LogInfo("Successfully stopped container: %s", containerId) - return true + return nil } func HandleInfo(w http.ResponseWriter, r *http.Request) { diff --git a/router/router.go b/router/router.go index a2c900d..848281e 100644 --- a/router/router.go +++ b/router/router.go @@ -18,14 +18,8 @@ func SetupRouter() *mux.Router { r.HandleFunc("/health", health.Handle).Methods("GET", "OPTIONS") r.HandleFunc("/node/info", node.HandleInfo).Methods("GET", "OPTIONS") - - /* - r.HandleFunc("/album", handler.GetAlbums).Methods("GET", "OPTIONS") - r.HandleFunc("/album", handler.SaveAlbum).Methods("POST", "OPTIONS") - r.HandleFunc("/album/{albumId}", handler.DeleteAlbum).Methods("DELETE", "OPTIONS") - r.HandleFunc("/album/{albumId}", handler.GetAlbum).Methods("GET", "OPTIONS") - r.HandleFunc("/shareAlbum/{albumId}/{userId}", handler.ShareAlbum).Methods("POST", "OPTIONS") - */ + r.HandleFunc("/node/start", node.HandleStart).Methods("POST", "OPTIONS") + r.HandleFunc("/node/stop", node.HandleStop).Methods("POST", "OPTIONS") return r } -- 2.49.1 From 8fc09925f219610f9c1dc3bf7e16c2544303a6c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gerg=C5=91=20Dulai?= Date: Wed, 4 Feb 2026 23:11:19 +0100 Subject: [PATCH 3/3] Set explicit socker url --- node/node.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/node/node.go b/node/node.go index dba42df..71f0e9c 100644 --- a/node/node.go +++ b/node/node.go @@ -127,9 +127,9 @@ func info() []container.Summary { } func openDockerClient() *client.Client { - cli, err := client.New( + cli, err := client.NewClientWithOpts( client.FromEnv, - client.WithAPIVersionFromEnv(), + client.WithHost("unix:///var/run/docker.sock"), ) if err != nil { log.LogFatalError("Failed to create Docker client: %v", err) -- 2.49.1