Skip to content

Commit

Permalink
all: Rework websocket interfaceo
Browse files Browse the repository at this point in the history
Rework the websocket interface to be a little more stateless, using
command + arguments in a single JSON burst and echoing that back with
the results. Not that it really matters since we only allow one
connection at at time.

Signed-off-by: StaticRocket <[email protected]>
  • Loading branch information
StaticRocket committed Nov 7, 2022
1 parent 2195450 commit 2aca6ab
Show file tree
Hide file tree
Showing 4 changed files with 161 additions and 109 deletions.
52 changes: 39 additions & 13 deletions seva-launcher/seva-compose.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,24 @@ import (
"github.com/melbahja/got"
)

func start_app() string {
type Containers []struct {
ID string `json:"ID"`
Name string `json:"Name"`
Command string `json:"Command"`
Project string `json:"Project"`
Service string `json:"Service"`
State string `json:"State"`
Health string `json:"Health"`
ExitCode int `json:"ExitCode"`
Publishers []struct {
URL string `json:"URL"`
TargetPort int `json:"TargetPort"`
PublishedPort int `json:"PublishedPort"`
Protocol string `json:"Protocol"`
} `json:"Publishers"`
}

func start_app(command WebSocketCommand) WebSocketCommand {
log.Println("Starting selected app")
cmd := exec.Command(docker_compose, "-p", "seva-launcher", "up", "-d")
output, err := cmd.CombinedOutput()
Expand All @@ -23,37 +40,42 @@ func start_app() string {
exit(1)
}
output_s := strings.TrimSpace(string(output))
command.Response = append(command.Response, strings.Split(output_s, "\n")...)
log.Printf("|\n%s\n", output_s)
return output_s
return command
}

func stop_app() string {
func stop_app(command WebSocketCommand) WebSocketCommand {
log.Println("Stopping selected app")
cmd := exec.Command(docker_compose, "-p", "seva-launcher", "down", "--remove-orphans")
output, err := cmd.CombinedOutput()
if err != nil {
log.Println("Failed to stop selected app! (It may not be running!)")
}
output_s := strings.TrimSpace(string(output))
command.Response = append(command.Response, strings.Split(output_s, "\n")...)
log.Printf("|\n%s\n", output_s)
return output_s
return command
}

func get_app() string {
func get_app(command WebSocketCommand) WebSocketCommand {
if _, err := os.Stat("metadata.json"); errors.Is(err, os.ErrNotExist) {
return "{}"
command.Response = append(command.Response, "{}")
return command
}
content, err := os.ReadFile("metadata.json")
if err != nil {
log.Println(err)
exit(1)
}
return string(content)
command.Response = []string{string(content)}
return command
}

func load_app(name string) string {
func load_app(command WebSocketCommand) WebSocketCommand {
name := command.Arguments[0]
log.Println("Loading " + name + " from store")
stop_app()
command = stop_app(command)

files := []string{"metadata.json", "docker-compose.yml"}
for _, element := range files {
Expand All @@ -76,10 +98,12 @@ func load_app(name string) string {
exit(1)
}
}
return string("0")
command.Response = append(command.Response, "0")
return command
}

func is_running(name string) string {
func is_running(command WebSocketCommand) WebSocketCommand {
name := command.Arguments[0]
log.Println("Checking if " + name + " is running")
cmd := exec.Command(docker_compose, "-p", "seva-launcher", "ps", "--format", "json")
output, err := cmd.Output()
Expand All @@ -95,8 +119,10 @@ func is_running(name string) string {
}
for _, element := range containers {
if element.Name == name {
return string("1")
command.Response = append(command.Response, "1")
return command
}
}
return string("0")
command.Response = append(command.Response, "0")
return command
}
73 changes: 1 addition & 72 deletions seva-launcher/seva-launcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,13 @@ import (
"syscall"

"github.com/gorilla/mux"
"github.com/gorilla/websocket"
"github.com/skratchdot/open-golang/open"
)

var store_url = "https://raw.githubusercontent.com/StaticRocket/seva-apps/main"
var addr = flag.String("addr", "0.0.0.0:8000", "http service address")
var no_browser = flag.Bool("no-browser", false, "do not launch browser")
var docker_browser = flag.Bool("docker-browser", false, "force use of docker browser")
var upgrader = websocket.Upgrader{}
var container_id_list [2]string
var docker_compose string

Expand All @@ -33,23 +31,6 @@ var content embed.FS
//go:embed docker-compose
var docker_compose_bin []byte

type Containers []struct {
ID string `json:"ID"`
Name string `json:"Name"`
Command string `json:"Command"`
Project string `json:"Project"`
Service string `json:"Service"`
State string `json:"State"`
Health string `json:"Health"`
ExitCode int `json:"ExitCode"`
Publishers []struct {
URL string `json:"URL"`
TargetPort int `json:"TargetPort"`
PublishedPort int `json:"PublishedPort"`
Protocol string `json:"Protocol"`
} `json:"Publishers"`
}

func is_docker_compose_installed() bool {
cmd := exec.Command("docker-compose", "-v")
_, err := cmd.CombinedOutput()
Expand All @@ -70,58 +51,6 @@ func prepare_compose() string {
return "docker-compose"
}

func echo(w http.ResponseWriter, r *http.Request) {
c, err := upgrader.Upgrade(w, r, nil)
if err != nil {
log.Print("upgrade:", err)
return
}
defer c.Close()
for {
_, message, err := c.ReadMessage()
if err != nil {
log.Println("read:", err)
break
}
log.Printf("recv: %s", message)
var resp = string("")
switch string(message) {
case "start_app":
resp = start_app()
case "load_app":
var name []byte
_, name, err = c.ReadMessage()
if err != nil {
log.Println("read:", err)
break
}
resp = load_app(string(name))
case "stop_app":
resp = stop_app()
case "get_app":
resp = get_app()
case "is_running":
var name []byte
_, name, err = c.ReadMessage()
if err != nil {
log.Println("read:", err)
break
}
resp = is_running(string(name))
default:
resp = "Ignoring invalid command"
log.Println(resp)
}
if resp != "" {
err = c.WriteMessage(websocket.TextMessage, []byte(resp))
if err != nil {
log.Println("write:", err)
break
}
}
}
}

func setup_working_directory() {
err := os.MkdirAll("/tmp/seva-launcher", os.ModePerm)
if err != nil {
Expand Down Expand Up @@ -213,7 +142,7 @@ func setup_exit_handler() {

func handle_requests() {
router := mux.NewRouter()
router.HandleFunc("/ws", echo)
router.HandleFunc("/ws", websocket_controller)
log.Println("Listening for websocket messages at " + *addr + "/ws")
root_content, err := fs.Sub(content, "web")
if err != nil {
Expand Down
71 changes: 71 additions & 0 deletions seva-launcher/seva-websocket.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/*
Main websocket control logic
*/
package main

import (
"encoding/json"
"log"
"net/http"

"github.com/gorilla/websocket"
)

var upgrader = websocket.Upgrader{}

type WebSocketCommand struct {
Command string `json:"command"`
Arguments []string `json:"arguments"`
ExitCode int `json:"exit_code"`
Response []string `json:"response"`
}

func websocket_controller(w http.ResponseWriter, r *http.Request) {
c, err := upgrader.Upgrade(w, r, nil)
if err != nil {
log.Print("upgrade:", err)
return
}
defer c.Close()
for {
_, message, err := c.ReadMessage()
if err != nil {
log.Println("read:", err)
break
}
log.Printf("recv: %s", message)

var command WebSocketCommand
err = json.Unmarshal(message, &command)
if err != nil {
log.Println(err)
resp := "Ignoring invalid command"
log.Println(resp)
command.ExitCode = 1
command.Response = append(command.Response, resp)
}

switch command.Command {
case "start_app":
command = start_app(command)
case "load_app":
command = load_app(command)
case "stop_app":
command = stop_app(command)
case "get_app":
command = get_app(command)
case "is_running":
command = is_running(command)
}

json, err := json.Marshal(command)
if err != nil {
log.Println("Error serializing response!")
}
err = c.WriteMessage(websocket.TextMessage, []byte(json))
if err != nil {
log.Println("write:", err)
break
}
}
}
Loading

0 comments on commit 2aca6ab

Please sign in to comment.