Adam Štrauch
676ddf2136
All checks were successful
continuous-integration/drone/push Build is passing
*Fix get action *Taking common out of modules * initial glue tests * dynamic docker sock for testing * Stats rewroked to be more encapsulated * Fix of stats gathering in background
445 lines
13 KiB
Go
445 lines
13 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"io/ioutil"
|
|
"log"
|
|
"net/http"
|
|
"os"
|
|
"strings"
|
|
|
|
"github.com/labstack/echo"
|
|
"github.com/rosti-cz/node-api/apps"
|
|
"github.com/rosti-cz/node-api/common"
|
|
"github.com/rosti-cz/node-api/glue"
|
|
)
|
|
|
|
func homeHandler(c echo.Context) error {
|
|
return c.Render(http.StatusOK, "index.html", templateData{
|
|
Token: config.Token,
|
|
})
|
|
}
|
|
|
|
func listAppsHandler(c echo.Context) error {
|
|
processor := glue.Processor{
|
|
DB: common.GetDBConnection(),
|
|
DockerSock: config.DockerSocket,
|
|
BindIPHTTP: config.AppsBindIPHTTP,
|
|
BindIPSSH: config.AppsBindIPSSH,
|
|
AppsPath: config.AppsPath,
|
|
}
|
|
|
|
applications, err := processor.List()
|
|
if err != nil {
|
|
return c.JSONPretty(http.StatusInternalServerError, Message{Message: err.Error()}, JSONIndent)
|
|
}
|
|
|
|
return c.JSON(http.StatusOK, applications)
|
|
}
|
|
|
|
// Returns one app
|
|
func getAppHandler(c echo.Context) error {
|
|
name := c.Param("name")
|
|
|
|
processor := glue.Processor{
|
|
AppName: name,
|
|
DB: common.GetDBConnection(),
|
|
DockerSock: config.DockerSocket,
|
|
BindIPHTTP: config.AppsBindIPHTTP,
|
|
BindIPSSH: config.AppsBindIPSSH,
|
|
AppsPath: config.AppsPath,
|
|
}
|
|
app, err := processor.Get()
|
|
if err != nil {
|
|
return c.JSONPretty(http.StatusInternalServerError, Message{Message: err.Error()}, JSONIndent)
|
|
}
|
|
|
|
return c.JSON(http.StatusOK, app)
|
|
}
|
|
|
|
// Create a new app
|
|
// If you add register_only=1 into query string, it won't start or create any container, just adds record into the database.
|
|
func createAppHandler(c echo.Context) error {
|
|
registerOnly := c.QueryParam("register_only") == "1"
|
|
|
|
appTemplate := apps.App{}
|
|
err := c.Bind(&appTemplate)
|
|
if err != nil {
|
|
return c.JSONPretty(http.StatusBadRequest, Message{Message: err.Error()}, JSONIndent)
|
|
}
|
|
|
|
processor := glue.Processor{
|
|
AppName: appTemplate.Name,
|
|
DB: common.GetDBConnection(),
|
|
DockerSock: config.DockerSocket,
|
|
BindIPHTTP: config.AppsBindIPHTTP,
|
|
BindIPSSH: config.AppsBindIPSSH,
|
|
AppsPath: config.AppsPath,
|
|
}
|
|
|
|
if registerOnly {
|
|
err = processor.Register(appTemplate)
|
|
if err != nil && strings.Contains(err.Error(), "validation error") {
|
|
return c.JSONPretty(http.StatusBadRequest, Message{Errors: []string{err.Error()}}, JSONIndent)
|
|
} else if err != nil {
|
|
return c.JSONPretty(http.StatusInternalServerError, Message{Message: err.Error()}, JSONIndent)
|
|
}
|
|
} else {
|
|
err = processor.Create(appTemplate)
|
|
if err != nil && strings.Contains(err.Error(), "validation error") {
|
|
return c.JSONPretty(http.StatusBadRequest, Message{Errors: []string{err.Error()}}, JSONIndent)
|
|
} else if err != nil {
|
|
return c.JSONPretty(http.StatusInternalServerError, Message{Message: err.Error()}, JSONIndent)
|
|
}
|
|
}
|
|
|
|
return c.JSON(http.StatusOK, Message{Message: "ok"})
|
|
}
|
|
|
|
// Update existing app
|
|
func updateAppHandler(c echo.Context) error {
|
|
name := c.Param("name")
|
|
|
|
appTemplate := apps.App{}
|
|
err := c.Bind(&appTemplate)
|
|
if err != nil {
|
|
return c.JSONPretty(http.StatusBadRequest, Message{Message: err.Error()}, JSONIndent)
|
|
}
|
|
|
|
processor := glue.Processor{
|
|
AppName: name,
|
|
DB: common.GetDBConnection(),
|
|
DockerSock: config.DockerSocket,
|
|
BindIPHTTP: config.AppsBindIPHTTP,
|
|
BindIPSSH: config.AppsBindIPSSH,
|
|
AppsPath: config.AppsPath,
|
|
}
|
|
err = processor.Update(appTemplate)
|
|
if err != nil && strings.Contains(err.Error(), "validation error") {
|
|
return c.JSONPretty(http.StatusBadRequest, Message{Errors: []string{err.Error()}}, JSONIndent)
|
|
} else if err != nil {
|
|
return c.JSONPretty(http.StatusInternalServerError, Message{Message: err.Error()}, JSONIndent)
|
|
}
|
|
|
|
return c.JSON(http.StatusOK, Message{Message: "ok"})
|
|
}
|
|
|
|
// Stop existing app
|
|
func stopAppHandler(c echo.Context) error {
|
|
name := c.Param("name")
|
|
|
|
processor := glue.Processor{
|
|
AppName: name,
|
|
DB: common.GetDBConnection(),
|
|
DockerSock: config.DockerSocket,
|
|
BindIPHTTP: config.AppsBindIPHTTP,
|
|
BindIPSSH: config.AppsBindIPSSH,
|
|
AppsPath: config.AppsPath,
|
|
}
|
|
err := processor.Stop()
|
|
if err != nil {
|
|
return c.JSONPretty(http.StatusInternalServerError, Message{Message: err.Error()}, JSONIndent)
|
|
}
|
|
|
|
return c.JSON(http.StatusOK, Message{Message: "ok"})
|
|
}
|
|
|
|
// Start existing app
|
|
func startAppHandler(c echo.Context) error {
|
|
name := c.Param("name")
|
|
|
|
processor := glue.Processor{
|
|
AppName: name,
|
|
DB: common.GetDBConnection(),
|
|
DockerSock: config.DockerSocket,
|
|
BindIPHTTP: config.AppsBindIPHTTP,
|
|
BindIPSSH: config.AppsBindIPSSH,
|
|
AppsPath: config.AppsPath,
|
|
}
|
|
err := processor.Start()
|
|
if err != nil {
|
|
return c.JSONPretty(http.StatusInternalServerError, Message{Message: err.Error()}, JSONIndent)
|
|
}
|
|
|
|
return c.JSON(http.StatusOK, Message{Message: "ok"})
|
|
}
|
|
|
|
// Stop existing app
|
|
func restartAppHandler(c echo.Context) error {
|
|
name := c.Param("name")
|
|
|
|
processor := glue.Processor{
|
|
AppName: name,
|
|
DB: common.GetDBConnection(),
|
|
DockerSock: config.DockerSocket,
|
|
BindIPHTTP: config.AppsBindIPHTTP,
|
|
BindIPSSH: config.AppsBindIPSSH,
|
|
AppsPath: config.AppsPath,
|
|
}
|
|
err := processor.Restart()
|
|
if err != nil {
|
|
return c.JSONPretty(http.StatusInternalServerError, Message{Message: err.Error()}, JSONIndent)
|
|
}
|
|
|
|
return c.JSON(http.StatusOK, Message{Message: "ok"})
|
|
}
|
|
|
|
// Set password for the app user in the container
|
|
func setPasswordHandler(c echo.Context) error {
|
|
name := c.Param("name")
|
|
|
|
password := Password{}
|
|
err := c.Bind(&password)
|
|
if err != nil {
|
|
return c.JSONPretty(http.StatusInternalServerError, Message{Message: err.Error()}, JSONIndent)
|
|
}
|
|
|
|
processor := glue.Processor{
|
|
AppName: name,
|
|
DB: common.GetDBConnection(),
|
|
DockerSock: config.DockerSocket,
|
|
BindIPHTTP: config.AppsBindIPHTTP,
|
|
BindIPSSH: config.AppsBindIPSSH,
|
|
AppsPath: config.AppsPath,
|
|
}
|
|
err = processor.SetPassword(password.Password)
|
|
if err != nil {
|
|
return c.JSONPretty(http.StatusInternalServerError, Message{Message: err.Error()}, JSONIndent)
|
|
}
|
|
|
|
return c.JSON(http.StatusOK, Message{Message: "ok"})
|
|
}
|
|
|
|
// Copies body of the request into /srv/.ssh/authorized_keys
|
|
func setKeysHandler(c echo.Context) error {
|
|
name := c.Param("name")
|
|
|
|
body, err := ioutil.ReadAll(c.Request().Body)
|
|
if err != nil {
|
|
return c.JSONPretty(http.StatusInternalServerError, Message{Message: err.Error()}, JSONIndent)
|
|
}
|
|
|
|
processor := glue.Processor{
|
|
AppName: name,
|
|
DB: common.GetDBConnection(),
|
|
DockerSock: config.DockerSocket,
|
|
BindIPHTTP: config.AppsBindIPHTTP,
|
|
BindIPSSH: config.AppsBindIPSSH,
|
|
AppsPath: config.AppsPath,
|
|
}
|
|
err = processor.UpdateKeys(string(body) + "\n")
|
|
if err != nil {
|
|
return c.JSONPretty(http.StatusInternalServerError, Message{Message: err.Error()}, JSONIndent)
|
|
}
|
|
|
|
return c.JSON(http.StatusOK, Message{Message: "ok"})
|
|
}
|
|
|
|
func setServicesHandler(c echo.Context) error {
|
|
name := c.Param("name")
|
|
|
|
tech := &Technology{}
|
|
err := c.Bind(tech)
|
|
if err != nil {
|
|
return c.JSONPretty(http.StatusBadRequest, Message{Message: err.Error()}, JSONIndent)
|
|
}
|
|
|
|
processor := glue.Processor{
|
|
AppName: name,
|
|
DB: common.GetDBConnection(),
|
|
DockerSock: config.DockerSocket,
|
|
BindIPHTTP: config.AppsBindIPHTTP,
|
|
BindIPSSH: config.AppsBindIPSSH,
|
|
AppsPath: config.AppsPath,
|
|
}
|
|
|
|
err = processor.EnableTech(tech.Name, tech.Version)
|
|
if err != nil {
|
|
return c.JSONPretty(http.StatusInternalServerError, Message{Message: err.Error()}, JSONIndent)
|
|
}
|
|
|
|
return c.JSON(http.StatusOK, Message{Message: "ok"})
|
|
}
|
|
|
|
// Rebuilds existing app, it keeps the data but created the container again
|
|
func rebuildAppHandler(c echo.Context) error {
|
|
name := c.Param("name")
|
|
|
|
processor := glue.Processor{
|
|
AppName: name,
|
|
DB: common.GetDBConnection(),
|
|
DockerSock: config.DockerSocket,
|
|
BindIPHTTP: config.AppsBindIPHTTP,
|
|
BindIPSSH: config.AppsBindIPSSH,
|
|
AppsPath: config.AppsPath,
|
|
}
|
|
err := processor.Rebuild()
|
|
if err != nil {
|
|
return c.JSONPretty(http.StatusInternalServerError, Message{Message: err.Error()}, JSONIndent)
|
|
}
|
|
|
|
return c.JSON(http.StatusOK, Message{Message: "ok"})
|
|
}
|
|
|
|
// Adds new label
|
|
func addLabelHandler(c echo.Context) error {
|
|
name := c.Param("name")
|
|
label := apps.Label{}
|
|
|
|
err := c.Bind(&label)
|
|
if err != nil {
|
|
return c.JSONPretty(http.StatusBadRequest, Message{Message: err.Error()}, JSONIndent)
|
|
}
|
|
|
|
processor := glue.Processor{
|
|
AppName: name,
|
|
DB: common.GetDBConnection(),
|
|
DockerSock: config.DockerSocket,
|
|
BindIPHTTP: config.AppsBindIPHTTP,
|
|
BindIPSSH: config.AppsBindIPSSH,
|
|
AppsPath: config.AppsPath,
|
|
}
|
|
err = processor.AddLabel(label.Value)
|
|
if err != nil {
|
|
return c.JSONPretty(http.StatusInternalServerError, Message{Message: err.Error()}, JSONIndent)
|
|
}
|
|
|
|
return c.JSON(http.StatusOK, Message{Message: "ok"})
|
|
}
|
|
|
|
// Removes existing label
|
|
func deleteLabelHandler(c echo.Context) error {
|
|
name := c.Param("name")
|
|
label := apps.Label{}
|
|
|
|
err := c.Bind(&label)
|
|
if err != nil {
|
|
return c.JSONPretty(http.StatusBadRequest, Message{Message: err.Error()}, JSONIndent)
|
|
}
|
|
|
|
processor := glue.Processor{
|
|
AppName: name,
|
|
DB: common.GetDBConnection(),
|
|
DockerSock: config.DockerSocket,
|
|
BindIPHTTP: config.AppsBindIPHTTP,
|
|
BindIPSSH: config.AppsBindIPSSH,
|
|
AppsPath: config.AppsPath,
|
|
}
|
|
err = processor.RemoveLabel(label.Value)
|
|
if err != nil {
|
|
return c.JSONPretty(http.StatusInternalServerError, Message{Message: err.Error()}, JSONIndent)
|
|
}
|
|
|
|
return c.JSON(http.StatusOK, Message{Message: "ok"})
|
|
}
|
|
|
|
// Delete one app
|
|
// This is async function.
|
|
func deleteAppHandler(c echo.Context) error {
|
|
name := c.Param("name")
|
|
|
|
go func(name string) {
|
|
processor := glue.Processor{
|
|
AppName: name,
|
|
DB: common.GetDBConnection(),
|
|
DockerSock: config.DockerSocket,
|
|
BindIPHTTP: config.AppsBindIPHTTP,
|
|
BindIPSSH: config.AppsBindIPSSH,
|
|
AppsPath: config.AppsPath,
|
|
}
|
|
err := processor.Delete()
|
|
if err != nil {
|
|
log.Printf("Deletion of application failed: %v", err)
|
|
}
|
|
}(name)
|
|
|
|
return c.JSON(http.StatusOK, Message{Message: "deleted"})
|
|
}
|
|
|
|
// Orphans returns directories in /srv that doesn't match any hosted application
|
|
func getOrphansHander(c echo.Context) error {
|
|
return c.JSON(http.StatusOK, []string{})
|
|
}
|
|
|
|
// Return info about the node including performance index
|
|
func getNodeInfoHandler(c echo.Context) error {
|
|
processor := glue.Processor{
|
|
DB: common.GetDBConnection(),
|
|
DockerSock: config.DockerSocket,
|
|
BindIPHTTP: config.AppsBindIPHTTP,
|
|
BindIPSSH: config.AppsBindIPSSH,
|
|
AppsPath: config.AppsPath,
|
|
NodeProcessor: &nodeProcessor,
|
|
}
|
|
node, err := processor.GetNode()
|
|
if err != nil {
|
|
return c.JSONPretty(http.StatusInternalServerError, Message{Message: err.Error()}, JSONIndent)
|
|
}
|
|
|
|
return c.JSON(http.StatusOK, node)
|
|
}
|
|
|
|
// Returns list of running processes (inside supervisor)
|
|
func getAppProcessesHandler(c echo.Context) error {
|
|
name := c.Param("name")
|
|
|
|
processor := glue.Processor{
|
|
AppName: name,
|
|
DB: common.GetDBConnection(),
|
|
DockerSock: config.DockerSocket,
|
|
BindIPHTTP: config.AppsBindIPHTTP,
|
|
BindIPSSH: config.AppsBindIPSSH,
|
|
AppsPath: config.AppsPath,
|
|
}
|
|
processes, err := processor.Processes()
|
|
if err != nil {
|
|
return c.JSONPretty(http.StatusInternalServerError, Message{Message: err.Error()}, JSONIndent)
|
|
}
|
|
|
|
return c.JSON(http.StatusOK, processes)
|
|
}
|
|
|
|
// Return metrics that are available for running node
|
|
func metricsHandler(c echo.Context) error {
|
|
var metrics string
|
|
|
|
// Node indexes
|
|
processor := glue.Processor{
|
|
DB: common.GetDBConnection(),
|
|
DockerSock: config.DockerSocket,
|
|
BindIPHTTP: config.AppsBindIPHTTP,
|
|
BindIPSSH: config.AppsBindIPSSH,
|
|
AppsPath: config.AppsPath,
|
|
NodeProcessor: &nodeProcessor,
|
|
}
|
|
node, err := processor.GetNode()
|
|
if err != nil {
|
|
return c.JSONPretty(http.StatusInternalServerError, Message{Message: err.Error()}, JSONIndent)
|
|
}
|
|
|
|
hostname, err := os.Hostname()
|
|
if err != nil {
|
|
return c.JSONPretty(http.StatusInternalServerError, Message{Message: err.Error()}, JSONIndent)
|
|
}
|
|
|
|
metrics += fmt.Sprintf("rosti_node_index{hostname=\"%s\"} %f\n", hostname, node.Index)
|
|
metrics += fmt.Sprintf("rosti_node_disk_space_index{hostname=\"%s\"} %f\n", hostname, node.DiskSpaceIndex)
|
|
metrics += fmt.Sprintf("rosti_node_load1_index{hostname=\"%s\"} %f\n", hostname, node.Load1Index)
|
|
metrics += fmt.Sprintf("rosti_node_load5_index{hostname=\"%s\"} %f\n", hostname, node.Load5Index)
|
|
metrics += fmt.Sprintf("rosti_node_load15_index{hostname=\"%s\"} %f\n", hostname, node.Load15Index)
|
|
metrics += fmt.Sprintf("rosti_node_memory_index{hostname=\"%s\"} %f\n", hostname, node.MemoryIndex)
|
|
metrics += fmt.Sprintf("rosti_node_sold_memory{hostname=\"%s\"} %d\n", hostname, node.SoldMemory)
|
|
|
|
apps, err := processor.List()
|
|
if err != nil {
|
|
return c.JSONPretty(http.StatusInternalServerError, Message{Message: err.Error()}, JSONIndent)
|
|
}
|
|
|
|
for _, app := range apps {
|
|
metrics += fmt.Sprintf("rosti_node_app_disk_usage_bytes{hostname=\"%s\", app=\"%s\"} %d\n", hostname, app.Name, app.DiskUsageBytes)
|
|
metrics += fmt.Sprintf("rosti_node_app_disk_usage_inodes{hostname=\"%s\", app=\"%s\"} %d\n", hostname, app.Name, app.DiskUsageInodes)
|
|
}
|
|
|
|
return c.String(http.StatusOK, metrics)
|
|
}
|