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:
128
docker/docker.go
Normal file
128
docker/docker.go
Normal 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
69
docker/prepare.go
Normal 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
105
docker/scale.go
Normal 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,
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user