package main import ( "log" "net/http" "time" "github.com/labstack/echo" "github.com/rosti-cz/node-api/apps" "github.com/rosti-cz/node-api/common" "github.com/rosti-cz/node-api/docker" "github.com/rosti-cz/node-api/node" ) // JSONIndent Indendation of JSON output format const JSONIndent = " " func main() { // Close database at the end db := common.GetDBConnection() defer db.Close() // Templating t := &Template{} // Stats loop go func() { for { err := gatherContainersStats() if err != nil { log.Println("LOOP ERROR:", err.Error()) } time.Sleep(5 * time.Minute) } }() // Node stats go func() { for { err := node.Log() if err != nil { log.Println("NODE PERFORMANCE LOG ERROR:", err.Error()) } time.Sleep(5 * time.Minute) } }() // API e := echo.New() e.Renderer = t // e.Use(TokenMiddleware) // Returns list of apps e.GET("/", func(c echo.Context) error { return c.Render(http.StatusOK, "index.html", "") }) e.GET("/v1/apps", func(c echo.Context) error { applications, err := apps.List() if err != nil { return c.JSONPretty(http.StatusInternalServerError, Message{Message: err.Error()}, JSONIndent) } return c.JSON(http.StatusOK, applications) }) // Returns one app e.GET("/v1/apps/:name", func(c echo.Context) error { name := c.Param("name") app, err := apps.Get(name) if err != nil { return c.JSONPretty(http.StatusInternalServerError, Message{Message: err.Error()}, JSONIndent) } return c.JSON(http.StatusOK, app) }) // Create a new app e.POST("/v1/apps", func(c echo.Context) error { app := apps.App{} err := c.Bind(&app) if err != nil { return c.JSONPretty(http.StatusBadRequest, Message{Message: err.Error()}, JSONIndent) } err = apps.New(app.Name, app.SSHPort, app.HTTPPort, app.Image, app.CPU, app.Memory) if err != nil { if validationError, ok := err.(apps.ValidationError); ok { return c.JSONPretty(http.StatusBadRequest, Message{Errors: validationError.Errors}, JSONIndent) } return c.JSONPretty(http.StatusInternalServerError, Message{Message: err.Error()}, JSONIndent) } container := docker.Container{ App: &app, } err = container.Create() if err != nil { return c.JSONPretty(http.StatusInternalServerError, Message{Message: err.Error()}, JSONIndent) } err = container.Start() if err != nil { return c.JSONPretty(http.StatusInternalServerError, Message{Message: err.Error()}, JSONIndent) } return c.JSON(http.StatusOK, Message{Message: "ok"}) }) // Update existing app e.PUT("/v1/apps/:name", func(c echo.Context) error { name := c.Param("name") app := apps.App{} err := c.Bind(&app) if err != nil { return c.JSONPretty(http.StatusBadRequest, Message{Message: err.Error()}, JSONIndent) } appPointer, err := apps.Update(name, app.SSHPort, app.HTTPPort, app.Image, app.CPU, app.Memory) if err != nil { if validationError, ok := err.(apps.ValidationError); ok { return c.JSONPretty(http.StatusBadRequest, Message{Errors: validationError.Errors}, JSONIndent) } return c.JSONPretty(http.StatusInternalServerError, Message{Message: err.Error()}, JSONIndent) } app = *appPointer container := docker.Container{ App: &app, } err = container.Destroy() if err != nil && err.Error() == "no container found" { // We don't care if the container didn't exist anyway err = nil } if err != nil { return c.JSONPretty(http.StatusInternalServerError, Message{Message: err.Error()}, JSONIndent) } err = container.Create() if err != nil { return c.JSONPretty(http.StatusInternalServerError, Message{Message: err.Error()}, JSONIndent) } go updateContainerStats(&app) return c.JSON(http.StatusOK, Message{Message: "ok"}) }) // Stop existing app e.PUT("/v1/apps/:name/stop", func(c echo.Context) error { name := c.Param("name") app, err := apps.Get(name) if err != nil { return c.JSONPretty(http.StatusInternalServerError, Message{Message: err.Error()}, JSONIndent) } container := docker.Container{ App: app, } err = container.Stop() if err != nil { return c.JSONPretty(http.StatusInternalServerError, Message{Message: err.Error()}, JSONIndent) } go updateContainerStats(app) return c.JSON(http.StatusOK, Message{Message: "ok"}) }) // Start existing app e.PUT("/v1/apps/:name/start", func(c echo.Context) error { name := c.Param("name") app, err := apps.Get(name) if err != nil { return c.JSONPretty(http.StatusInternalServerError, Message{Message: err.Error()}, JSONIndent) } container := docker.Container{ App: app, } err = container.Start() if err != nil { return c.JSONPretty(http.StatusInternalServerError, Message{Message: err.Error()}, JSONIndent) } go updateContainerStats(app) return c.JSON(http.StatusOK, Message{Message: "ok"}) }) // Stop existing app e.PUT("/v1/apps/:name/restart", func(c echo.Context) error { name := c.Param("name") app, err := apps.Get(name) if err != nil { return c.JSONPretty(http.StatusInternalServerError, Message{Message: err.Error()}, JSONIndent) } container := docker.Container{ App: app, } err = container.Restart() if err != nil { return c.JSONPretty(http.StatusInternalServerError, Message{Message: err.Error()}, JSONIndent) } go updateContainerStats(app) return c.JSON(http.StatusOK, Message{Message: "ok"}) }) // Stats for existing app e.GET("/v1/apps/:name/stats", func(c echo.Context) error { name := c.Param("name") app, err := apps.Get(name) if err != nil { return c.JSONPretty(http.StatusInternalServerError, Message{Message: err.Error()}, JSONIndent) } container := docker.Container{ App: app, } cpu, memory, err := container.ResourceUsage() if err != nil { return c.JSONPretty(http.StatusInternalServerError, Message{Message: err.Error()}, JSONIndent) } log.Println("DEBUG", cpu, memory) return c.JSON(http.StatusOK, Message{Message: "ok"}) }) // Rebuilds existing app, it keeps the data but created the container again e.PUT("/v1/apps/:name/rebuild", func(c echo.Context) error { name := c.Param("name") app, err := apps.Get(name) if err != nil { return c.JSONPretty(http.StatusInternalServerError, Message{Message: err.Error()}, JSONIndent) } container := docker.Container{ App: app, } err = container.Destroy() if err != nil && err.Error() == "no container found" { // We don't care if the container didn't exist anyway err = nil } if err != nil { return c.JSONPretty(http.StatusInternalServerError, Message{Message: err.Error()}, JSONIndent) } err = container.Create() if err != nil { return c.JSONPretty(http.StatusInternalServerError, Message{Message: err.Error()}, JSONIndent) } go updateContainerStats(app) return c.JSON(http.StatusOK, Message{Message: "ok"}) }) // Adds new label e.POST("/v1/apps/:name/labels", func(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) } err = apps.AddLabel(name, 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 e.DELETE("/v1/apps/:name/labels", func(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) } err = apps.RemoveLabel(name, 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 e.DELETE("/v1/apps/:name", func(c echo.Context) error { name := c.Param("name") app, err := apps.Get(name) if err != nil { return c.JSONPretty(http.StatusInternalServerError, Message{Message: err.Error()}, JSONIndent) } container := docker.Container{ App: app, } err = container.Delete() if err != nil { return c.JSONPretty(http.StatusInternalServerError, Message{Message: err.Error()}, JSONIndent) } err = apps.Delete(name) if err != nil { return c.JSONPretty(http.StatusInternalServerError, Message{Message: err.Error()}, JSONIndent) } return c.JSON(http.StatusOK, Message{Message: "deleted"}) }) // Orphans returns directories in /srv that doesn't match any hosted application e.GET("/v1/orphans", func(c echo.Context) error { return c.JSON(http.StatusOK, []string{}) }) // Return info about the node including performance index e.GET("/v1/node", func(c echo.Context) error { node, err := node.GetNodeInfo() if err != nil { return c.JSONPretty(http.StatusInternalServerError, Message{Message: err.Error()}, JSONIndent) } return c.JSON(http.StatusOK, node) }) e.Logger.Fatal(e.Start("127.0.0.1:1323")) }