Rest of the endpoints finished
This commit is contained in:
parent
0181236e23
commit
4f9dac7f5c
32
api.http
32
api.http
@ -3,8 +3,8 @@ Content-type: application/json
|
|||||||
|
|
||||||
{
|
{
|
||||||
"name": "test_1234",
|
"name": "test_1234",
|
||||||
"ssh_port": 10000,
|
"ssh_port": 46500,
|
||||||
"http_port": 10001,
|
"http_port": 46501,
|
||||||
"image": "docker.io/rosti/runtime:2020.04-1",
|
"image": "docker.io/rosti/runtime:2020.04-1",
|
||||||
"cpu": 100,
|
"cpu": 100,
|
||||||
"memory": 128000
|
"memory": 128000
|
||||||
@ -16,8 +16,8 @@ PUT http://localhost:1323/v1/apps/test_1234
|
|||||||
Content-type: application/json
|
Content-type: application/json
|
||||||
|
|
||||||
{
|
{
|
||||||
"ssh_port": 36500,
|
"ssh_port": 46500,
|
||||||
"http_port": 36501
|
"http_port": 46501
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -25,3 +25,27 @@ Content-type: application/json
|
|||||||
|
|
||||||
PUT http://localhost:1323/v1/apps/test_1234/start
|
PUT http://localhost:1323/v1/apps/test_1234/start
|
||||||
Content-type: application/json
|
Content-type: application/json
|
||||||
|
|
||||||
|
###
|
||||||
|
|
||||||
|
|
||||||
|
PUT http://localhost:1323/v1/apps/test_1234/stop
|
||||||
|
Content-type: application/json
|
||||||
|
|
||||||
|
###
|
||||||
|
|
||||||
|
DELETE http://localhost:1323/v1/apps/test_1234
|
||||||
|
Content-type: application/json
|
||||||
|
|
||||||
|
###
|
||||||
|
|
||||||
|
|
||||||
|
GET http://localhost:1323/v1/apps/test_1234/stats
|
||||||
|
Content-type: application/json
|
||||||
|
|
||||||
|
|
||||||
|
###
|
||||||
|
|
||||||
|
|
||||||
|
GET http://localhost:1323/v1/apps
|
||||||
|
Content-type: application/json
|
||||||
|
@ -23,6 +23,13 @@ type Label struct {
|
|||||||
Value string `json:"value"`
|
Value string `json:"value"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AppState contains info about runnint application, it's not saved in the database
|
||||||
|
type AppState struct {
|
||||||
|
State string `json:"state"`
|
||||||
|
CPUUsage int `json:"cpu_usage"`
|
||||||
|
MemoryUsage int `json:"memory_usage"`
|
||||||
|
}
|
||||||
|
|
||||||
// App keeps info about hosted application
|
// App keeps info about hosted application
|
||||||
type App struct {
|
type App struct {
|
||||||
gorm.Model
|
gorm.Model
|
||||||
@ -34,6 +41,8 @@ type App struct {
|
|||||||
CPU int `json:"cpu"` // percentage, 200 means two CPU
|
CPU int `json:"cpu"` // percentage, 200 means two CPU
|
||||||
Memory int `json:"memory"` // Limit in MB
|
Memory int `json:"memory"` // Limit in MB
|
||||||
// Labels []Label `json:"labels"` // username:cx or user_id:1
|
// Labels []Label `json:"labels"` // username:cx or user_id:1
|
||||||
|
|
||||||
|
State AppState `json:"state" gorm:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate do basic checks of the struct values
|
// Validate do basic checks of the struct values
|
||||||
|
@ -7,7 +7,6 @@ import (
|
|||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -25,7 +24,7 @@ const dockerTimeout = 10
|
|||||||
const dockerSock = "unix:///var/run/docker.sock"
|
const dockerSock = "unix:///var/run/docker.sock"
|
||||||
|
|
||||||
// DOCKER_API_VERSION set API version of Docker, 1.40 belongs to Docker 19.03.11
|
// DOCKER_API_VERSION set API version of Docker, 1.40 belongs to Docker 19.03.11
|
||||||
const dockerAPIVersion = "1.40"
|
const dockerAPIVersion = "1.24"
|
||||||
|
|
||||||
// Driver keeps everything for connection to Docker
|
// Driver keeps everything for connection to Docker
|
||||||
type Driver struct{}
|
type Driver struct{}
|
||||||
@ -76,8 +75,10 @@ func (d *Driver) Status(name string) (string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
containerID, err := d.nameToID(name)
|
containerID, err := d.nameToID(name)
|
||||||
|
if err != nil && err.Error() == "no container found" {
|
||||||
|
return "no-container", err
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
status = "no-container"
|
|
||||||
return status, err
|
return status, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -117,7 +118,9 @@ func (d *Driver) Stats(name string) (float64, int, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return 0.0, 0, err
|
return 0.0, 0, err
|
||||||
}
|
}
|
||||||
log.Println(data)
|
// It returns one JSON:
|
||||||
|
// {"read":"2020-07-11T20:42:31.486726241Z","preread":"2020-07-11T20:42:30.484048602Z","pids_stats":{"current":7},"blkio_stats":{"io_service_bytes_recursive":[{"major":253,"minor":0,"op":"Read","value":0},{"major":253,"minor":0,"op":"Write","value":20480},{"major":253,"minor":0,"op":"Sync","value":12288},{"major":253,"minor":0,"op":"Async","value":8192},{"major":253,"minor":0,"op":"Discard","value":0},{"major":253,"minor":0,"op":"Total","value":20480}],"io_serviced_recursive":[{"major":253,"minor":0,"op":"Read","value":0},{"major":253,"minor":0,"op":"Write","value":5},{"major":253,"minor":0,"op":"Sync","value":3},{"major":253,"minor":0,"op":"Async","value":2},{"major":253,"minor":0,"op":"Discard","value":0},{"major":253,"minor":0,"op":"Total","value":5}],"io_queue_recursive":[],"io_service_time_recursive":[],"io_wait_time_recursive":[],"io_merged_recursive":[],"io_time_recursive":[],"sectors_recursive":[]},"num_procs":0,"storage_stats":{},"cpu_stats":{"cpu_usage":{"total_usage":758392753,"percpu_usage":[302688474,0,11507116,124238500,222136766,5656446,3009320,0,19406386,1397028,6201423,62151294,0,0,0,0],"usage_in_kernelmode":100000000,"usage_in_usermode":640000000},"system_cpu_usage":119385810000000,"online_cpus":12,"throttling_data":{"periods":21,"throttled_periods":1,"throttled_time":2995938}},"precpu_stats":{"cpu_usage":{"total_usage":758282347,"percpu_usage":[302688474,0,11507116,124238500,222026360,5656446,3009320,0,19406386,1397028,6201423,62151294,0,0,0,0],"usage_in_kernelmode":100000000,"usage_in_usermode":640000000},"system_cpu_usage":119373720000000,"online_cpus":12,"throttling_data":{"periods":21,"throttled_periods":1,"throttled_time":2995938}},"memory_stats":{"usage":21626880,"max_usage":22630400,"stats":{"active_anon":15949824,"active_file":0,"cache":0,"dirty":0,"hierarchical_memory_limit":144179200,"hierarchical_memsw_limit":288358400,"inactive_anon":0,"inactive_file":0,"mapped_file":0,"pgfault":13167,"pgmajfault":0,"pgpgin":7293,"pgpgout":3406,"rss":15900672,"rss_huge":0,"total_active_anon":15949824,"total_active_file":0,"total_cache":0,"total_dirty":0,"total_inactive_anon":0,"total_inactive_file":0,"total_mapped_file":0,"total_pgfault":13167,"total_pgmajfault":0,"total_pgpgin":7293,"total_pgpgout":3406,"total_rss":15900672,"total_rss_huge":0,"total_unevictable":0,"total_writeback":0,"unevictable":0,"writeback":0},"limit":144179200},"name":"/test_1234","id":"576878d645efecc8e5e2a57b88351f7b5c551e3fc72dc8473fd965d10dfddbec","networks":{"eth0":{"rx_bytes":6150,"rx_packets":37,"rx_errors":0,"rx_dropped":0,"tx_bytes":0,"tx_packets":0,"tx_errors":0,"tx_dropped":0}}}
|
||||||
|
log.Println(string(data))
|
||||||
|
|
||||||
return 0.0, 0, nil
|
return 0.0, 0, nil
|
||||||
}
|
}
|
||||||
@ -248,24 +251,26 @@ func (d *Driver) Create(name string, image string, volumePath string, HTTPPort i
|
|||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
portmaps := make(nat.PortMap, 1)
|
portmaps := nat.PortMap{}
|
||||||
|
|
||||||
portbindingsHTTP := make([]nat.PortBinding, 1)
|
// portbindingsHTTP := make([]nat.PortBinding, 1)
|
||||||
portbindingsHTTP[0] = nat.PortBinding{
|
// portbindingsHTTP[0] = nat.PortBinding{
|
||||||
HostPort: strconv.Itoa(HTTPPort) + "/tcp",
|
// HostIP: "0.0.0.0",
|
||||||
}
|
// HostPort: strconv.Itoa(HTTPPort) + "/tcp",
|
||||||
portmaps["8000/tcp"] = portbindingsHTTP
|
// }
|
||||||
|
// portmaps["8000/tcp"] = portbindingsHTTP
|
||||||
|
|
||||||
if SSHPort != 0 {
|
// if SSHPort != 0 {
|
||||||
portbindingsSSH := make([]nat.PortBinding, 1)
|
// portbindingsSSH := make([]nat.PortBinding, 1)
|
||||||
portbindingsSSH[0] = nat.PortBinding{
|
// portbindingsSSH[0] = nat.PortBinding{
|
||||||
HostPort: strconv.Itoa(SSHPort) + "/tcp",
|
// HostIP: "0.0.0.0",
|
||||||
}
|
// HostPort: strconv.Itoa(SSHPort) + "/tcp",
|
||||||
portmaps["22/tcp"] = portbindingsSSH
|
// }
|
||||||
}
|
// portmaps["22/tcp"] = portbindingsSSH
|
||||||
|
// }
|
||||||
|
|
||||||
createdContainer, err := cli.ContainerCreate(
|
createdContainer, err := cli.ContainerCreate(
|
||||||
context.TODO(),
|
context.Background(),
|
||||||
&container.Config{
|
&container.Config{
|
||||||
Hostname: name,
|
Hostname: name,
|
||||||
Env: []string{},
|
Env: []string{},
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package docker
|
package docker
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"log"
|
||||||
"path"
|
"path"
|
||||||
|
|
||||||
"github.com/rosti-cz/apps-api/apps"
|
"github.com/rosti-cz/apps-api/apps"
|
||||||
@ -8,7 +9,7 @@ import (
|
|||||||
|
|
||||||
// Container extends App struct from App
|
// Container extends App struct from App
|
||||||
type Container struct {
|
type Container struct {
|
||||||
App apps.App `json:"app"`
|
App *apps.App `json:"app"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Container) getDriver() *Driver {
|
func (c *Container) getDriver() *Driver {
|
||||||
@ -21,8 +22,26 @@ func (c *Container) volumeHostPath() string {
|
|||||||
return path.Join("/srv", c.App.Name)
|
return path.Join("/srv", c.App.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetApp app object with populated state fields
|
||||||
|
func (c *Container) GetApp() (*apps.App, error) {
|
||||||
|
status, err := c.Status()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
state := apps.AppState{
|
||||||
|
State: status,
|
||||||
|
CPUUsage: 0,
|
||||||
|
MemoryUsage: 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
c.App.State = state
|
||||||
|
|
||||||
|
return c.App, nil
|
||||||
|
}
|
||||||
|
|
||||||
// Status returns state of the container
|
// Status returns state of the container
|
||||||
// Possible values: running, stopped, data-only, unknown
|
// Possible values: running, stopped, no-container, unknown
|
||||||
func (c *Container) Status() (string, error) {
|
func (c *Container) Status() (string, error) {
|
||||||
status := "unknown"
|
status := "unknown"
|
||||||
|
|
||||||
@ -32,15 +51,14 @@ func (c *Container) Status() (string, error) {
|
|||||||
|
|
||||||
driver := c.getDriver()
|
driver := c.getDriver()
|
||||||
containerStatus, err := driver.Status(c.App.Name)
|
containerStatus, err := driver.Status(c.App.Name)
|
||||||
|
if err != nil && err.Error() == "no container found" {
|
||||||
|
return "no-container", nil
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return status, err
|
return status, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if containerStatus == "no-container" {
|
|
||||||
status = "data-only"
|
|
||||||
} else {
|
|
||||||
status = containerStatus
|
status = containerStatus
|
||||||
}
|
|
||||||
|
|
||||||
return status, nil
|
return status, nil
|
||||||
}
|
}
|
||||||
@ -121,6 +139,9 @@ func (c *Container) Delete() error {
|
|||||||
|
|
||||||
volumePath := path.Join("/srv", c.App.Name)
|
volumePath := path.Join("/srv", c.App.Name)
|
||||||
err = removeDirectory(volumePath)
|
err = removeDirectory(volumePath)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
|
|
||||||
return err
|
return nil
|
||||||
}
|
}
|
||||||
|
128
main.go
128
main.go
@ -1,6 +1,7 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/labstack/echo"
|
"github.com/labstack/echo"
|
||||||
@ -25,13 +26,25 @@ func main() {
|
|||||||
|
|
||||||
// Returns list of apps
|
// Returns list of apps
|
||||||
e.GET("/v1/apps", func(c echo.Context) error {
|
e.GET("/v1/apps", func(c echo.Context) error {
|
||||||
apps, err := apps.List()
|
applications, err := apps.List()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return c.JSONPretty(http.StatusInternalServerError, Message{Message: err.Error()}, JSONIndent)
|
return c.JSONPretty(http.StatusInternalServerError, Message{Message: err.Error()}, JSONIndent)
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.JSON(http.StatusOK, apps)
|
populatedApps := []*apps.App{}
|
||||||
|
for _, app := range *applications {
|
||||||
|
container := docker.Container{
|
||||||
|
App: &app,
|
||||||
|
}
|
||||||
|
populatedApp, err := container.GetApp()
|
||||||
|
if err != nil {
|
||||||
|
return c.JSONPretty(http.StatusInternalServerError, Message{Message: err.Error()}, JSONIndent)
|
||||||
|
}
|
||||||
|
populatedApps = append(populatedApps, populatedApp)
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.JSON(http.StatusOK, populatedApps)
|
||||||
})
|
})
|
||||||
|
|
||||||
// Returns one app
|
// Returns one app
|
||||||
@ -43,6 +56,14 @@ func main() {
|
|||||||
return c.JSONPretty(http.StatusInternalServerError, Message{Message: err.Error()}, JSONIndent)
|
return c.JSONPretty(http.StatusInternalServerError, Message{Message: err.Error()}, JSONIndent)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
container := docker.Container{
|
||||||
|
App: app,
|
||||||
|
}
|
||||||
|
_, err = container.GetApp()
|
||||||
|
if err != nil {
|
||||||
|
return c.JSONPretty(http.StatusInternalServerError, Message{Message: err.Error()}, JSONIndent)
|
||||||
|
}
|
||||||
|
|
||||||
return c.JSON(http.StatusOK, app)
|
return c.JSON(http.StatusOK, app)
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -63,7 +84,7 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
container := docker.Container{
|
container := docker.Container{
|
||||||
App: app,
|
App: &app,
|
||||||
}
|
}
|
||||||
|
|
||||||
err = container.Create()
|
err = container.Create()
|
||||||
@ -100,7 +121,7 @@ func main() {
|
|||||||
app = *appPointer
|
app = *appPointer
|
||||||
|
|
||||||
container := docker.Container{
|
container := docker.Container{
|
||||||
App: app,
|
App: &app,
|
||||||
}
|
}
|
||||||
|
|
||||||
err = container.Destroy()
|
err = container.Destroy()
|
||||||
@ -123,7 +144,23 @@ func main() {
|
|||||||
|
|
||||||
// Stop existing app
|
// Stop existing app
|
||||||
e.PUT("/v1/apps/:name/stop", func(c echo.Context) error {
|
e.PUT("/v1/apps/:name/stop", func(c echo.Context) error {
|
||||||
return c.JSON(http.StatusOK, map[string]string{})
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.JSON(http.StatusOK, Message{Message: "ok"})
|
||||||
})
|
})
|
||||||
|
|
||||||
// Start existing app
|
// Start existing app
|
||||||
@ -136,7 +173,7 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
container := docker.Container{
|
container := docker.Container{
|
||||||
App: *app,
|
App: app,
|
||||||
}
|
}
|
||||||
|
|
||||||
err = container.Start()
|
err = container.Start()
|
||||||
@ -149,19 +186,92 @@ func main() {
|
|||||||
|
|
||||||
// Stop existing app
|
// Stop existing app
|
||||||
e.PUT("/v1/apps/:name/restart", func(c echo.Context) error {
|
e.PUT("/v1/apps/:name/restart", func(c echo.Context) error {
|
||||||
return c.JSON(http.StatusOK, map[string]string{})
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
// Rebuilds existing app, it keeps the data but created the container again
|
||||||
e.PUT("/v1/apps/:name/rebuild", func(c echo.Context) error {
|
e.PUT("/v1/apps/:name/rebuild", func(c echo.Context) error {
|
||||||
return c.JSON(http.StatusOK, map[string]string{})
|
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 {
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.JSON(http.StatusOK, Message{Message: "ok"})
|
||||||
})
|
})
|
||||||
|
|
||||||
// Delete one app
|
// Delete one app
|
||||||
e.DELETE("/v1/apps/:name", func(c echo.Context) error {
|
e.DELETE("/v1/apps/:name", func(c echo.Context) error {
|
||||||
name := c.Param("name")
|
name := c.Param("name")
|
||||||
|
|
||||||
err := apps.Delete(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 {
|
if err != nil {
|
||||||
return c.JSONPretty(http.StatusInternalServerError, Message{Message: err.Error()}, JSONIndent)
|
return c.JSONPretty(http.StatusInternalServerError, Message{Message: err.Error()}, JSONIndent)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user