I am not proud of it.

Why did I choose to do this.
I could get some more sleep but I choose to write this.
I just want some snapshot before I start breaking stuff.
This commit is contained in:
rony5394
2026-01-31 21:54:14 +01:00
commit 91c16cf429
10 changed files with 504 additions and 0 deletions

128
docker/docker.go Normal file
View File

@@ -0,0 +1,128 @@
package docker
import (
"context"
"encoding/json"
"fmt"
"os"
"os/signal"
"strings"
"sync"
"syscall"
"time"
"net/http"
"github.com/moby/moby/client"
)
// Add mutex.
var ApiClient *client.Client;
var scale sync.Map;
var token string = "12345";
type aService struct{
ServiceId string `json:"serviceId"`;
VolumeNames []string `json:"volumeNames"`;
Node string `json:"node"`;
}
func Run(){
ctx, _ := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM);
var err error;
ApiClient, err = client.New(client.FromEnv);
if(err != nil){
panic("Docker client was not able to init from env!" + err.Error());
}
info, err := ApiClient.Info(context.Background(), client.InfoOptions{});
if(err != nil){
panic("Error getting info!" + err.Error());
}
if(!info.Info.Swarm.ControlAvailable){
panic("Node is not a swarm manager.");
}
server := &http.Server{
Addr: ":1234",
}
http.HandleFunc("/services", listServices);
http.HandleFunc("/scale/down", scaleDown);
http.HandleFunc("/scale/up", scaleUp);
http.HandleFunc("/prepare", prepare);
go func(){
err = server.ListenAndServe();
if err == http.ErrServerClosed {
return;
}
if(err != nil){
panic("Unable to start http server!" + err.Error());
}
}();
time.Sleep(10*time.Millisecond);
<-ctx.Done();
fmt.Println("Stopping http server.");
server.Close();
fmt.Println("Exiting!");
}
func bearerAuth(w http.ResponseWriter, r *http.Request)bool {
authHeader := r.Header.Get("Authorization")
expected := "Bearer " + token
if authHeader != expected {
w.WriteHeader(http.StatusUnauthorized)
fmt.Fprintln(w, "Unauthorized")
return false;
}
return true;
}
func listServices(w http.ResponseWriter, r *http.Request){
if(r.Method != http.MethodGet){
w.WriteHeader(http.StatusMethodNotAllowed);
return;
}
if !bearerAuth(w,r) {return};
list, err := ApiClient.ServiceList(context.Background(), client.ServiceListOptions{});
if(err != nil){
panic("Unable to list services!" + err.Error());
}
var services []aService;
for _, service:= range list.Items{
var settings map[string]string = service.Spec.Labels;
if(settings["blazena.enable"] != "true"){
continue;
}
targetVolumes := strings.Split(settings["blazena.volumes"], ",");
services = append(services, aService{
ServiceId: service.ID,
VolumeNames: targetVolumes,
Node: settings["blazena.node"],
});
}
bytes, err := json.Marshal(services);
if err != nil{
panic("Error during response encoding!" + err.Error());
}
fmt.Fprint(w, string(bytes));
}

69
docker/prepare.go Normal file
View File

@@ -0,0 +1,69 @@
package docker
import (
"context"
"encoding/json"
"fmt"
"io"
"net/http"
"github.com/moby/moby/api/types/swarm"
"github.com/moby/moby/api/types/mount"
"github.com/moby/moby/client"
)
func prepare(w http.ResponseWriter, r *http.Request){
if r.Method != http.MethodPost{
w.WriteHeader(http.StatusMethodNotAllowed);
fmt.Fprint(w, "Method Not Allowed");
return;
}
//TODO: add token auth
rawBody, err := io.ReadAll(r.Body);
if err != nil {
panic("Failed to read body!");
}
var bodyDecoded struct{
volume string
node string
};
err = json.Unmarshal(rawBody, &bodyDecoded);
if err != nil {
panic("Failed to unmarshal json."+ err.Error());
}
maxConcurrent := uint64(1);
totalCompletions := uint64(1);
targetVolume := bodyDecoded.volume;
targetNode := bodyDecoded.node;
ApiClient.ServiceCreate(context.Background(), client.ServiceCreateOptions{
Spec: swarm.ServiceSpec{
Mode: swarm.ServiceMode{
ReplicatedJob: &swarm.ReplicatedJob{
MaxConcurrent: &maxConcurrent,
TotalCompletions: &totalCompletions,
},
},
TaskTemplate: swarm.TaskSpec{
ContainerSpec: &swarm.ContainerSpec{
Image: "docker.io/library/alpine:latest",
Mounts: []mount.Mount{
mount.Mount{
Source: targetVolume,
Target: "/volume",
Type: "bind",
},
},
},
Placement: &swarm.Placement{
Constraints: []string{"node.hostname=="+targetNode},
},
},
},
});
}

105
docker/scale.go Normal file
View File

@@ -0,0 +1,105 @@
package docker;
import(
"net/http"
"io"
"encoding/json"
"context"
"github.com/moby/moby/client"
);
func scaleDown(w http.ResponseWriter, r *http.Request){
if r.Method != http.MethodPost {
w.WriteHeader(http.StatusMethodNotAllowed);
return;
}
if !bearerAuth(w, r) {return}
rawBody, err := io.ReadAll(r.Body);
if err != nil {
panic("Failed to read body!" + err.Error());
}
var parsedBody struct{
ServiceId string `json:"serviceId"`;
};
err = json.Unmarshal(rawBody, &parsedBody);
if err != nil{
panic("Failed to unmarshal request body!"+ err.Error());
}
inspectresoult, err := ApiClient.ServiceInspect(context.Background(), parsedBody.ServiceId, client.ServiceInspectOptions{});
if err != nil{
panic("Error inspecting service!"+ err.Error());
}
originalScale := inspectresoult.Service.Spec.Mode.Replicated.Replicas;
updatedSpec := inspectresoult.Service.Spec;
newScale := uint64(0);
updatedSpec.Mode.Replicated.Replicas = &newScale;
scale.Store(parsedBody.ServiceId, *originalScale);
_, err = ApiClient.ServiceUpdate(context.Background(), parsedBody.ServiceId, client.ServiceUpdateOptions{
Spec: updatedSpec,
Version: inspectresoult.Service.Version,
});
if(err != nil){
panic("Failed to update service."+ err.Error());
}
}
func scaleUp(w http.ResponseWriter, r *http.Request){
if r.Method != http.MethodPost {
w.WriteHeader(http.StatusMethodNotAllowed);
return;
}
if !bearerAuth(w, r) {return}
rawBody, err := io.ReadAll(r.Body);
if err != nil {
panic("Failed to read body!");
}
var parsedBody struct{
ServiceId string `json:"serviceId"`;
};
err = json.Unmarshal(rawBody, &parsedBody);
if err != nil{
panic("Failed to unmarshal request body!"+ err.Error());
}
inspectresoult, err := ApiClient.ServiceInspect(context.Background(), parsedBody.ServiceId, client.ServiceInspectOptions{});
if err != nil{
panic("Error inspecting service!"+ err.Error());
}
originalScale, ok := scale.Load(parsedBody.ServiceId);
if(!ok){
panic("Its not okay!");
}
originalScaleChecked, ok := originalScale.(uint64);
if(!ok){
panic("Its very not okay!")
}
updatedSpec := inspectresoult.Service.Spec;
updatedSpec.Mode.Replicated.Replicas = &originalScaleChecked;
ApiClient.ServiceUpdate(context.Background(), parsedBody.ServiceId, client.ServiceUpdateOptions{
Spec: updatedSpec,
Version: inspectresoult.Service.Version,
});
}