Compare commits

4 Commits
main ... dev

Author SHA1 Message Date
d507198c2f Create license 2026-02-09 23:29:30 +01:00
8fc09925f2 Set explicit socker url 2026-02-04 23:11:19 +01:00
1369b3cf8b Container start/stop impl 2026-02-03 22:03:08 +01:00
b8761de020 Node endpoints with container handling 2026-02-03 21:09:41 +01:00
7 changed files with 166 additions and 10 deletions

1
container/container.go Normal file
View File

@@ -0,0 +1 @@
package container

8
docker-compose.yml Normal file
View File

@@ -0,0 +1,8 @@
version: "3.9"
services:
simple-cluster-node:
build: .
env_file:
- .env
ports:
- "8080:8080"

4
go.mod
View File

@@ -5,7 +5,7 @@ go 1.25.6
require ( require (
github.com/google/uuid v1.6.0 github.com/google/uuid v1.6.0
github.com/gorilla/mux v1.8.1 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 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/go-logr/stdr v1.2.2 // indirect
github.com/joho/godotenv v1.5.1 github.com/joho/godotenv v1.5.1
github.com/moby/docker-image-spec v1.3.1 // indirect 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/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.1.1 // indirect github.com/opencontainers/image-spec v1.1.1 // indirect
go.opentelemetry.io/auto/sdk v1.1.0 // indirect go.opentelemetry.io/auto/sdk v1.1.0 // indirect

4
go.sum
View File

@@ -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/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 h1:00BtlJY4MXkkt84WhUZPRqt5TvPbgig2FZvTbe3igYg=
github.com/moby/moby/api v1.52.0/go.mod h1:8mb+ReTlisw4pS6BRzCMts5M49W5M7bKt1cJy/YbAqc= 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 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.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 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040= github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040=

8
license Normal file
View File

@@ -0,0 +1,8 @@
The MIT License (MIT)
Copyright © 2026 Gergő Dulai
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

138
node/node.go Normal file
View File

@@ -0,0 +1,138 @@
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) {
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/start request body! (%s)\n%s", requestId, err)
w.WriteHeader(http.StatusBadRequest)
return
}
if containerId == "" {
log.LogError("Container id mus be specified for node/start! (%s)", requestId)
w.WriteHeader(http.StatusBadRequest)
return
}
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 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 {
return err
}
return nil
}
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.NewClientWithOpts(
client.FromEnv,
client.WithHost("unix:///var/run/docker.sock"),
)
if err != nil {
log.LogFatalError("Failed to create Docker client: %v", err)
}
return cli
}

View File

@@ -5,6 +5,7 @@ import (
"net/http" "net/http"
"os" "os"
"simple-cluster-node/health" "simple-cluster-node/health"
"simple-cluster-node/node"
"github.com/google/uuid" "github.com/google/uuid"
"github.com/gorilla/mux" "github.com/gorilla/mux"
@@ -14,15 +15,11 @@ import (
func SetupRouter() *mux.Router { func SetupRouter() *mux.Router {
r := mux.NewRouter() r := mux.NewRouter()
r.Use(identifyRequest, corsCheck, logRequest) r.Use(identifyRequest, corsCheck, logRequest)
r.HandleFunc("/health", health.Handle).Methods("GET", "OPTIONS")
/* r.HandleFunc("/health", health.Handle).Methods("GET", "OPTIONS")
r.HandleFunc("/album", handler.GetAlbums).Methods("GET", "OPTIONS") r.HandleFunc("/node/info", node.HandleInfo).Methods("GET", "OPTIONS")
r.HandleFunc("/album", handler.SaveAlbum).Methods("POST", "OPTIONS") r.HandleFunc("/node/start", node.HandleStart).Methods("POST", "OPTIONS")
r.HandleFunc("/album/{albumId}", handler.DeleteAlbum).Methods("DELETE", "OPTIONS") r.HandleFunc("/node/stop", node.HandleStop).Methods("POST", "OPTIONS")
r.HandleFunc("/album/{albumId}", handler.GetAlbum).Methods("GET", "OPTIONS")
r.HandleFunc("/shareAlbum/{albumId}/{userId}", handler.ShareAlbum).Methods("POST", "OPTIONS")
*/
return r return r
} }