kubedoom/kubedoom.go
2021-08-19 17:40:54 +01:00

180 lines
4.3 KiB
Go

package main
import (
"flag"
"log"
"net"
"os"
"os/exec"
"strconv"
"strings"
"time"
)
func hash(input string) int32 {
var hash int32
hash = 5381
for _, char := range input {
hash = ((hash << 5) + hash + int32(char))
}
if hash < 0 {
hash = 0 - hash
}
return hash
}
func runCmd(cmdstring string) {
parts := strings.Split(cmdstring, " ")
cmd := exec.Command(parts[0], parts[1:]...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err := cmd.Run()
if err != nil {
log.Fatalf("The following command failed: \"%v\"\n", cmdstring)
}
}
func outputCmd(argv []string) string {
cmd := exec.Command(argv[0], argv[1:]...)
cmd.Stderr = os.Stderr
output, err := cmd.Output()
if err != nil {
log.Fatalf("The following command failed: \"%v\"\n", argv)
}
return string(output)
}
func startCmd(cmdstring string) {
parts := strings.Split(cmdstring, " ")
cmd := exec.Command(parts[0], parts[1:]...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
cmd.Stdin = os.Stdin
err := cmd.Start()
if err != nil {
log.Fatalf("The following command failed: \"%v\"\n", cmdstring)
}
}
type Mode interface {
getEntities() []string
deleteEntity(string)
}
type podmode struct {
}
func (m podmode) getEntities() []string {
var args []string
if namespace, exists := os.LookupEnv("NAMESPACE"); exists {
args = []string{"kubectl", "get", "pods", "--namespace", namespace, "-o", "go-template", "--template={{range .items}}{{.metadata.namespace}}/{{.metadata.name}} {{end}}"}
} else {
args = []string{"kubectl", "get", "pods", "-A", "-o", "go-template", "--template={{range .items}}{{.metadata.namespace}}/{{.metadata.name}} {{end}}"}
}
output := outputCmd(args)
outputstr := strings.TrimSpace(output)
pods := strings.Split(outputstr, " ")
return pods
}
func (m podmode) deleteEntity(entity string) {
log.Printf("Pod to kill: %v", entity)
podparts := strings.Split(entity, "/")
cmd := exec.Command("/usr/bin/kubectl", "delete", "pod", "-n", podparts[0], podparts[1])
go cmd.Run()
}
type nsmode struct {
}
func (m nsmode) getEntities() []string {
args := []string{"kubectl", "get", "namespaces", "-o", "go-template", "--template={{range .items}}{{.metadata.name}} {{end}}"}
output := outputCmd(args)
outputstr := strings.TrimSpace(output)
namespaces := strings.Split(outputstr, " ")
return namespaces
}
func (m nsmode) deleteEntity(entity string) {
log.Printf("Namespace to kill: %v", entity)
cmd := exec.Command("/usr/bin/kubectl", "delete", "namespace", entity)
go cmd.Run()
}
func socketLoop(listener net.Listener, mode Mode) {
for {
conn, err := listener.Accept()
if err != nil {
panic(err)
}
stop := false
for !stop {
bytes := make([]byte, 40960)
n, err := conn.Read(bytes)
if err != nil {
stop = true
}
bytes = bytes[0:n]
strbytes := strings.TrimSpace(string(bytes))
entities := mode.getEntities()
if strbytes == "list" {
for _, entity := range entities {
padding := strings.Repeat("\n", 255-len(entity))
_, err = conn.Write([]byte(entity + padding))
if err != nil {
log.Fatal("Could not write to socker file")
}
}
conn.Close()
stop = true
} else if strings.HasPrefix(strbytes, "kill ") {
parts := strings.Split(strbytes, " ")
killhash, err := strconv.ParseInt(parts[1], 10, 32)
if err != nil {
log.Fatal("Could not parse kill hash")
}
for _, entity := range entities {
if hash(entity) == int32(killhash) {
mode.deleteEntity(entity)
break
}
}
conn.Close()
stop = true
}
}
}
}
func main() {
var modeFlag string
flag.StringVar(&modeFlag, "mode", "pods", "What to kill pods|namespaces")
flag.Parse()
var mode Mode
switch modeFlag {
case "pods":
mode = podmode{}
case "namespaces":
mode = nsmode{}
default:
log.Fatalf("Mode should be pods or namespaces")
}
listener, err := net.Listen("unix", "/dockerdoom.socket")
if err != nil {
log.Fatalf("Could not create socket file")
}
log.Print("Create virtual display")
startCmd("/usr/bin/Xvfb :99 -ac -screen 0 640x480x24")
time.Sleep(time.Duration(2) * time.Second)
startCmd("x11vnc -geometry 640x480 -forever -usepw -display :99")
log.Print("You can now connect to it with a VNC viewer at port 5900")
log.Print("Trying to start DOOM ...")
startCmd("/usr/bin/env DISPLAY=:99 /usr/local/games/psdoom -warp -E1M1 -skill 1 -nomouse")
socketLoop(listener, mode)
}