Handling apps via NATS messaging
This commit is contained in:
parent
e9c46f5d5c
commit
162daf6cba
1
.gitignore
vendored
1
.gitignore
vendored
@ -3,3 +3,4 @@ node-api
|
|||||||
.history/
|
.history/
|
||||||
api-node-17.http
|
api-node-17.http
|
||||||
*-packr.go
|
*-packr.go
|
||||||
|
*.sqlite
|
||||||
|
21
.vscode/launch.json
vendored
Normal file
21
.vscode/launch.json
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
// Use IntelliSense to learn about possible attributes.
|
||||||
|
// Hover to view descriptions of existing attributes.
|
||||||
|
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"name": "Launch Package",
|
||||||
|
"type": "go",
|
||||||
|
"request": "launch",
|
||||||
|
"mode": "debug",
|
||||||
|
"program": "${workspaceFolder}",
|
||||||
|
"env": {
|
||||||
|
"NATS_URL": "nats://192.168.122.127:4222",
|
||||||
|
"NATS_ALIAS": "node-x",
|
||||||
|
"DATABASE_PATH": "./node-x.sqlite",
|
||||||
|
"TOKEN": "ABCD",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
2
api.http
2
api.http
@ -1,5 +1,5 @@
|
|||||||
POST http://localhost:1323/v1/apps
|
POST http://localhost:1323/v1/apps
|
||||||
Authorization: Token fee60059-f554-4c35-b44f-74b6be377095
|
Authorization: Token ABCD
|
||||||
Content-type: application/json
|
Content-type: application/json
|
||||||
|
|
||||||
{
|
{
|
||||||
|
10
auth.go
10
auth.go
@ -1,7 +1,6 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"log"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/labstack/echo"
|
"github.com/labstack/echo"
|
||||||
@ -9,13 +8,6 @@ import (
|
|||||||
|
|
||||||
var skipPaths []string = []string{"/metrics"}
|
var skipPaths []string = []string{"/metrics"}
|
||||||
|
|
||||||
var configuredToken string
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
configuredToken = setToken()
|
|
||||||
log.Println("Access token:", configuredToken)
|
|
||||||
}
|
|
||||||
|
|
||||||
// TokenMiddleware handles authentication
|
// TokenMiddleware handles authentication
|
||||||
func TokenMiddleware(next echo.HandlerFunc) echo.HandlerFunc {
|
func TokenMiddleware(next echo.HandlerFunc) echo.HandlerFunc {
|
||||||
return func(c echo.Context) error {
|
return func(c echo.Context) error {
|
||||||
@ -34,7 +26,7 @@ func TokenMiddleware(next echo.HandlerFunc) echo.HandlerFunc {
|
|||||||
token = c.QueryParam("token")
|
token = c.QueryParam("token")
|
||||||
}
|
}
|
||||||
|
|
||||||
if (token != configuredToken || configuredToken == "") && !skip {
|
if (token != config.Token || config.Token == "") && !skip {
|
||||||
return c.JSONPretty(403, map[string]string{"message": "access denied"}, " ")
|
return c.JSONPretty(403, map[string]string{"message": "access denied"}, " ")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,9 +8,13 @@ import (
|
|||||||
|
|
||||||
// Config keeps info about configuration of this daemon
|
// Config keeps info about configuration of this daemon
|
||||||
type Config struct {
|
type Config struct {
|
||||||
|
Token string `envconfig:"TOKEN" required:"true"`
|
||||||
AppsPath string `envconfig:"APPS_PATH" default:"/srv"` // Where applications are located
|
AppsPath string `envconfig:"APPS_PATH" default:"/srv"` // Where applications are located
|
||||||
AppsBindIPHTTP string `envconfig:"APPS_BIND_IP_HTTP" default:"0.0.0.0"` // On what IP apps' HTTP port gonna be bound
|
AppsBindIPHTTP string `envconfig:"APPS_BIND_IP_HTTP" default:"0.0.0.0"` // On what IP apps' HTTP port gonna be bound
|
||||||
AppsBindIPSSH string `envconfig:"APPS_BIND_IP_SSH" default:"0.0.0.0"` // On what IP apps' SSH ports gonna be bound
|
AppsBindIPSSH string `envconfig:"APPS_BIND_IP_SSH" default:"0.0.0.0"` // On what IP apps' SSH ports gonna be bound
|
||||||
|
NATSURL string `envconfig:"NATS_URL" required:"true"`
|
||||||
|
NATSAlias string `envconfig:"NATS_ALIAS" required:"true"` // name/alias of the instance, ex. node-18
|
||||||
|
DatabasePath string `envconfig:"DATABASE_PATH" default:"/var/lib/node-api/rosti.db"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetConfig return configuration created based on environment variables
|
// GetConfig return configuration created based on environment variables
|
||||||
|
@ -13,7 +13,9 @@ var db *gorm.DB
|
|||||||
func init() {
|
func init() {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
db, err = gorm.Open("sqlite3", "/var/lib/node-api/rosti.db")
|
config := GetConfig()
|
||||||
|
|
||||||
|
db, err = gorm.Open("sqlite3", config.DatabasePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalln(err)
|
log.Fatalln(err)
|
||||||
}
|
}
|
||||||
|
41
config.go
41
config.go
@ -1,41 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io/ioutil"
|
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
uuid "github.com/satori/go.uuid"
|
|
||||||
)
|
|
||||||
|
|
||||||
const configDirectory = "/var/lib/node-api"
|
|
||||||
const tokenFilename = "/var/lib/node-api/token"
|
|
||||||
|
|
||||||
func setToken() string {
|
|
||||||
if _, err := os.Stat(configDirectory); os.IsNotExist(err) {
|
|
||||||
err = os.MkdirAll(configDirectory, 0700)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalln(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load token from the file
|
|
||||||
var token string
|
|
||||||
|
|
||||||
if _, err := os.Stat(tokenFilename); os.IsNotExist(err) {
|
|
||||||
token = uuid.NewV4().String()
|
|
||||||
|
|
||||||
err = ioutil.WriteFile(tokenFilename, []byte(token), 0600)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalln(err)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
tokenRaw, err := ioutil.ReadFile(tokenFilename)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalln(err)
|
|
||||||
}
|
|
||||||
token = string(tokenRaw)
|
|
||||||
}
|
|
||||||
|
|
||||||
return token
|
|
||||||
}
|
|
6
contrib/test_messages.sh
Normal file
6
contrib/test_messages.sh
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
nats pub --count 1 admin.apps.node-x.requests -w '{"type": "list"}'
|
||||||
|
nats pub --count 1 admin.apps.node-x.requests -w '{"type": "get", "app_name": "test_1234"}'
|
||||||
|
|
||||||
|
nats pub --count 1 admin.apps.node-x.requests -w '{"type": "create", "app_name": "natstest_0004", "payload": {"name": "natstest_0004", "image": "docker.io/rosti/runtime:2020.09-1", "cpu": 50, "memory": 256, "ssh_port": 20004, "http_port": 30004}}'
|
6
go.mod
6
go.mod
@ -3,17 +3,21 @@ module github.com/rosti-cz/node-api
|
|||||||
go 1.14
|
go 1.14
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/Microsoft/go-winio v0.4.18 // indirect
|
||||||
|
github.com/StackExchange/wmi v0.0.0-20210224194228-fe8f1750fd46 // indirect
|
||||||
github.com/docker/distribution v2.7.1+incompatible // indirect
|
github.com/docker/distribution v2.7.1+incompatible // indirect
|
||||||
github.com/docker/docker v1.13.1
|
github.com/docker/docker v1.13.1
|
||||||
github.com/docker/go-connections v0.4.0
|
github.com/docker/go-connections v0.4.0
|
||||||
github.com/docker/go-units v0.4.0 // indirect
|
github.com/docker/go-units v0.4.0 // indirect
|
||||||
|
github.com/go-ole/go-ole v1.2.5 // indirect
|
||||||
github.com/gobuffalo/packr v1.30.1
|
github.com/gobuffalo/packr v1.30.1
|
||||||
github.com/jinzhu/gorm v1.9.14
|
github.com/jinzhu/gorm v1.9.14
|
||||||
github.com/kelseyhightower/envconfig v1.4.0
|
github.com/kelseyhightower/envconfig v1.4.0
|
||||||
github.com/labstack/echo v3.3.10+incompatible
|
github.com/labstack/echo v3.3.10+incompatible
|
||||||
github.com/labstack/gommon v0.3.0 // indirect
|
github.com/labstack/gommon v0.3.0 // indirect
|
||||||
|
github.com/nats-io/nats.go v1.10.0
|
||||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||||
github.com/pkg/errors v0.9.1 // indirect
|
github.com/pkg/errors v0.9.1
|
||||||
github.com/satori/go.uuid v1.2.0
|
github.com/satori/go.uuid v1.2.0
|
||||||
github.com/shirou/gopsutil v2.20.6+incompatible
|
github.com/shirou/gopsutil v2.20.6+incompatible
|
||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 // indirect
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 // indirect
|
||||||
|
22
go.sum
22
go.sum
@ -3,6 +3,8 @@ github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc
|
|||||||
github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=
|
github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=
|
||||||
github.com/Microsoft/go-winio v0.4.14 h1:+hMXMk01us9KgxGb7ftKQt2Xpf5hH/yky+TDA+qxleU=
|
github.com/Microsoft/go-winio v0.4.14 h1:+hMXMk01us9KgxGb7ftKQt2Xpf5hH/yky+TDA+qxleU=
|
||||||
github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
|
github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
|
||||||
|
github.com/Microsoft/go-winio v0.4.18 h1:yjwCO1nhWEShaA5qsmPOBzAOjRCa2PRLsDNZ5yBWXpg=
|
||||||
|
github.com/Microsoft/go-winio v0.4.18/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84=
|
||||||
github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc=
|
github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc=
|
||||||
github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
||||||
github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=
|
github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=
|
||||||
@ -11,6 +13,8 @@ github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV
|
|||||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
|
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
|
||||||
github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d h1:G0m3OIz70MZUWq3EgK3CesDbo8upS2Vm9/P3FtgI+Jk=
|
github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d h1:G0m3OIz70MZUWq3EgK3CesDbo8upS2Vm9/P3FtgI+Jk=
|
||||||
github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
|
github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
|
||||||
|
github.com/StackExchange/wmi v0.0.0-20210224194228-fe8f1750fd46 h1:5sXbqlSomvdjlRbWyNqkPsJ3Fg+tQZCbgeX1VGljbQY=
|
||||||
|
github.com/StackExchange/wmi v0.0.0-20210224194228-fe8f1750fd46/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
|
||||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||||
github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
|
github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
|
||||||
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
|
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
|
||||||
@ -41,6 +45,8 @@ github.com/gin-gonic/gin v1.3.0/go.mod h1:7cKuhb5qV2ggCFctp2fJQ+ErvciLZrIeoOSOm6
|
|||||||
github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM=
|
github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM=
|
||||||
github.com/go-ole/go-ole v1.2.4 h1:nNBDSCOigTSiarFpYE9J/KtEA1IOW4CNeqT9TQDqCxI=
|
github.com/go-ole/go-ole v1.2.4 h1:nNBDSCOigTSiarFpYE9J/KtEA1IOW4CNeqT9TQDqCxI=
|
||||||
github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM=
|
github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM=
|
||||||
|
github.com/go-ole/go-ole v1.2.5 h1:t4MGB5xEDZvXI+0rMjjsfBsD7yAgp/s9ZDkL1JndXwY=
|
||||||
|
github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
||||||
github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=
|
github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=
|
||||||
github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg=
|
github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg=
|
||||||
github.com/go-openapi/jsonpointer v0.19.3 h1:gihV7YNZK1iK6Tgwwsxo2rJbD1GTbdm72325Bq8FI3w=
|
github.com/go-openapi/jsonpointer v0.19.3 h1:gihV7YNZK1iK6Tgwwsxo2rJbD1GTbdm72325Bq8FI3w=
|
||||||
@ -121,6 +127,15 @@ github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrk
|
|||||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||||
|
github.com/nats-io/jwt v0.3.2 h1:+RB5hMpXUUA2dfxuhBTEkMOrYmM+gKIZYS1KjSostMI=
|
||||||
|
github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU=
|
||||||
|
github.com/nats-io/nats.go v1.10.0 h1:L8qnKaofSfNFbXg0C5F71LdjPRnmQwSsA4ukmkt1TvY=
|
||||||
|
github.com/nats-io/nats.go v1.10.0/go.mod h1:AjGArbfyR50+afOUotNX2Xs5SYHf+CoOa5HH1eEl2HE=
|
||||||
|
github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
|
||||||
|
github.com/nats-io/nkeys v0.1.4 h1:aEsHIssIk6ETN5m2/MD8Y4B2X7FfXrBAUdkyRvbVYzA=
|
||||||
|
github.com/nats-io/nkeys v0.1.4/go.mod h1:XdZpAbhgyyODYqjTawOnIOI7VlbKSarI9Gfy1tqEu/s=
|
||||||
|
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
|
||||||
|
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
|
||||||
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
||||||
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
||||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||||
@ -138,6 +153,7 @@ github.com/shirou/gopsutil v2.20.6+incompatible h1:P37G9YH8M4vqkKcwBosp+URN5O8Ta
|
|||||||
github.com/shirou/gopsutil v2.20.6+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
|
github.com/shirou/gopsutil v2.20.6+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
|
||||||
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
|
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
|
||||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||||
|
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||||
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
||||||
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||||
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
|
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
|
||||||
@ -176,7 +192,9 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
|
|||||||
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.0.0-20191205180655-e7c4368fe9dd/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20191205180655-e7c4368fe9dd/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
|
golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
|
||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
@ -210,8 +228,12 @@ golang.org/x/sys v0.0.0-20190610200419-93c9922d18ae/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||||||
golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a h1:aYOabOQFp6Vj6W1F80affTUvO9UxmJRx8K0gsfABByQ=
|
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a h1:aYOabOQFp6Vj6W1F80affTUvO9UxmJRx8K0gsfABByQ=
|
||||||
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884=
|
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884=
|
||||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c h1:VwygUrnw9jn88c4u8GD3rZQbqrP/tgas88tPUbBxQrk=
|
||||||
|
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
||||||
|
@ -15,7 +15,7 @@ import (
|
|||||||
|
|
||||||
func homeHandler(c echo.Context) error {
|
func homeHandler(c echo.Context) error {
|
||||||
return c.Render(http.StatusOK, "index.html", templateData{
|
return c.Render(http.StatusOK, "index.html", templateData{
|
||||||
Token: configuredToken,
|
Token: config.Token,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -48,11 +48,6 @@ func getAppHandler(c echo.Context) error {
|
|||||||
return c.JSONPretty(http.StatusInternalServerError, Message{Message: err.Error()}, JSONIndent)
|
return c.JSONPretty(http.StatusInternalServerError, Message{Message: err.Error()}, JSONIndent)
|
||||||
}
|
}
|
||||||
|
|
||||||
app, err = apps.Get(name)
|
|
||||||
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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
648
handlers_nats.go
Normal file
648
handlers_nats.go
Normal file
@ -0,0 +1,648 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"github.com/nats-io/nats.go"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"github.com/rosti-cz/node-api/apps"
|
||||||
|
"github.com/rosti-cz/node-api/docker"
|
||||||
|
"github.com/rosti-cz/node-api/node"
|
||||||
|
)
|
||||||
|
|
||||||
|
// This handler only passes messages to another function for easier testing
|
||||||
|
func messageHandler(msg *nats.Msg) {
|
||||||
|
go _messageHandler(msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _messageHandler(m *nats.Msg) error {
|
||||||
|
message := RequestMessage{}
|
||||||
|
err := json.Unmarshal(m.Data, &message)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(errors.Wrap(err, "invalid JSON data in the incoming message"))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fmt.Printf("Received a message: %v\n", message)
|
||||||
|
|
||||||
|
eventHandlerMap := map[string](func(m *nats.Msg, message *RequestMessage) error){
|
||||||
|
"list": listEventHandler,
|
||||||
|
"get": getEventHandler,
|
||||||
|
"create": createEventHandler,
|
||||||
|
"update": updateEventHandler,
|
||||||
|
"delete": deleteEventHandler,
|
||||||
|
"stop": stopEventHandler,
|
||||||
|
"start": startEventHandler,
|
||||||
|
"restart": restartEventHandler,
|
||||||
|
"update_keys": updateKeysEventHandler,
|
||||||
|
"set_password": setPasswordEventHandler,
|
||||||
|
"processes": processesEventHandler,
|
||||||
|
"enable_tech": enableTechEventHandler,
|
||||||
|
"rebuild": rebuildEventHandler,
|
||||||
|
"add_label": addLabelEventHandler,
|
||||||
|
"remove_label": removeLabelEventHandler,
|
||||||
|
"list_orphans": listOrphansEventHandler,
|
||||||
|
"node": getNoteEventHandler,
|
||||||
|
}
|
||||||
|
|
||||||
|
if eventHandler, ok := eventHandlerMap[message.Type]; ok {
|
||||||
|
return eventHandler(m, &message)
|
||||||
|
} else {
|
||||||
|
log.Println("ERROR: event handler not defined for " + message.Type)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set password for the app user in the container
|
||||||
|
|
||||||
|
// Application processes
|
||||||
|
|
||||||
|
// Enable one of the supported technologies or services (python, node, redis, ...)
|
||||||
|
// Rebuilds existing app, it keeps the data but creates the container again
|
||||||
|
// Adds new label
|
||||||
|
// Removes existing label
|
||||||
|
|
||||||
|
// Orphans returns directories in /srv that doesn't match any hosted application
|
||||||
|
// Return info about the node including performance index
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns list of apps
|
||||||
|
func listEventHandler(m *nats.Msg, message *RequestMessage) error {
|
||||||
|
log.Println("> List")
|
||||||
|
|
||||||
|
err := gatherStates()
|
||||||
|
if err != nil {
|
||||||
|
return errorReplyFormater(m, "backend error", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
applications, err := apps.List()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return errorReplyFormater(m, "backend error", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
reply := ReplyMessage{
|
||||||
|
Payload: applications,
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := json.Marshal(reply)
|
||||||
|
if err != nil {
|
||||||
|
return errorReplyFormater(m, "reply formatter error", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = m.Respond(data)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("ERROR: list apps:", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns one app
|
||||||
|
func getEventHandler(m *nats.Msg, message *RequestMessage) error {
|
||||||
|
|
||||||
|
err := updateState(message.AppName)
|
||||||
|
if err != nil {
|
||||||
|
return errorReplyFormater(m, "backend error", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
app, err := apps.Get(message.AppName)
|
||||||
|
if err != nil {
|
||||||
|
return errorReplyFormater(m, "backend error", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
reply := ReplyMessage{
|
||||||
|
AppName: app.Name,
|
||||||
|
Payload: app,
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := json.Marshal(reply)
|
||||||
|
if err != nil {
|
||||||
|
return errorReplyFormater(m, "reply formatter error", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = m.Respond(data)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("ERROR: get app:", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a new app
|
||||||
|
func createEventHandler(m *nats.Msg, message *RequestMessage) error {
|
||||||
|
|
||||||
|
appTemplate := apps.App{}
|
||||||
|
body := []byte(message.Payload)
|
||||||
|
err := json.Unmarshal(body, &appTemplate)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("ERROR create application problem: " + err.Error())
|
||||||
|
publish(appTemplate.Name, "payload parsing problem", true)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = apps.New(appTemplate.Name, appTemplate.SSHPort, appTemplate.HTTPPort, appTemplate.Image, appTemplate.CPU, appTemplate.Memory)
|
||||||
|
if err != nil {
|
||||||
|
if validationError, ok := err.(apps.ValidationError); ok {
|
||||||
|
log.Println("ERROR create application problem: " + validationError.Error())
|
||||||
|
publish(appTemplate.Name, "validation problem", true)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
log.Println("ERROR create application problem: " + err.Error())
|
||||||
|
publish(appTemplate.Name, "backend problem", true)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
container := docker.Container{
|
||||||
|
App: &appTemplate,
|
||||||
|
}
|
||||||
|
|
||||||
|
err = container.Create()
|
||||||
|
if err != nil {
|
||||||
|
log.Println("ERROR create application problem: " + err.Error())
|
||||||
|
publish(appTemplate.Name, "backend problem", true)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = container.Start()
|
||||||
|
if err != nil {
|
||||||
|
log.Println("ERROR create application problem: " + err.Error())
|
||||||
|
publish(appTemplate.Name, "backend problem", true)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
publish(appTemplate.Name, "created", false)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update existing app
|
||||||
|
func updateEventHandler(m *nats.Msg, message *RequestMessage) error {
|
||||||
|
|
||||||
|
appTemplate := apps.App{}
|
||||||
|
body := []byte(message.Payload)
|
||||||
|
err := json.Unmarshal(body, &appTemplate)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("ERROR update application problem: " + err.Error())
|
||||||
|
publish(appTemplate.Name, "payload parsing problem", true)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
app, err := apps.Update(message.AppName, appTemplate.SSHPort, appTemplate.HTTPPort, appTemplate.Image, appTemplate.CPU, appTemplate.Memory)
|
||||||
|
if err != nil {
|
||||||
|
if validationError, ok := err.(apps.ValidationError); ok {
|
||||||
|
log.Println("ERROR update application problem: " + validationError.Error())
|
||||||
|
publish(appTemplate.Name, "backend problem", true)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
log.Println("ERROR update application problem: " + err.Error())
|
||||||
|
publish(appTemplate.Name, "backend problem", true)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
|
log.Println("ERROR update application problem: " + err.Error())
|
||||||
|
publish(appTemplate.Name, "backend problem", true)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = container.Create()
|
||||||
|
if err != nil {
|
||||||
|
log.Println("ERROR update application problem: " + err.Error())
|
||||||
|
publish(appTemplate.Name, "backend problem", true)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = container.Start()
|
||||||
|
if err != nil {
|
||||||
|
log.Println("ERROR update application problem: " + err.Error())
|
||||||
|
publish(appTemplate.Name, "backend problem", true)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
publish(appTemplate.Name, "updated", false)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete one app
|
||||||
|
func deleteEventHandler(m *nats.Msg, message *RequestMessage) error {
|
||||||
|
app, err := apps.Get(message.AppName)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("ERROR: delete app:", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
container := docker.Container{
|
||||||
|
App: app,
|
||||||
|
}
|
||||||
|
|
||||||
|
status, err := container.Status()
|
||||||
|
if err != nil {
|
||||||
|
log.Println("ERROR delete application problem: " + err.Error())
|
||||||
|
publish(app.Name, "backend problem", true)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if status != "no-container" {
|
||||||
|
// We stop the container first
|
||||||
|
err = container.Stop()
|
||||||
|
if err != nil {
|
||||||
|
log.Println("ERROR delete application problem: " + err.Error())
|
||||||
|
publish(app.Name, "backend problem", true)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then delete it
|
||||||
|
err = container.Delete()
|
||||||
|
if err != nil {
|
||||||
|
log.Println("ERROR delete application problem: " + err.Error())
|
||||||
|
publish(app.Name, "backend problem", true)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err = apps.Delete(app.Name)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("ERROR delete application problem: " + err.Error())
|
||||||
|
publish(app.Name, "backend problem", true)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
publish(app.Name, "deleted", false)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop existing app
|
||||||
|
func stopEventHandler(m *nats.Msg, message *RequestMessage) error {
|
||||||
|
app, err := apps.Get(message.AppName)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("ERROR stop application problem: " + err.Error())
|
||||||
|
publish(app.Name, "backend problem", true)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
container := docker.Container{
|
||||||
|
App: app,
|
||||||
|
}
|
||||||
|
|
||||||
|
status, err := container.Status()
|
||||||
|
if err != nil {
|
||||||
|
log.Println("ERROR stop application problem: " + err.Error())
|
||||||
|
publish(app.Name, "backend problem", true)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop the container only when it exists
|
||||||
|
if status != "no-container" {
|
||||||
|
err = container.Stop()
|
||||||
|
if err != nil {
|
||||||
|
log.Println("ERROR stop application problem: " + err.Error())
|
||||||
|
publish(app.Name, "backend problem", true)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
publish(app.Name, "stopped", false)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start existing app
|
||||||
|
func startEventHandler(m *nats.Msg, message *RequestMessage) error {
|
||||||
|
app, err := apps.Get(message.AppName)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("ERROR start application problem: " + err.Error())
|
||||||
|
publish(app.Name, "backend problem", true)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
container := docker.Container{
|
||||||
|
App: app,
|
||||||
|
}
|
||||||
|
|
||||||
|
status, err := container.Status()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if status == "no-container" {
|
||||||
|
err = container.Create()
|
||||||
|
if err != nil {
|
||||||
|
log.Println("ERROR start application problem: " + err.Error())
|
||||||
|
publish(app.Name, "backend problem", true)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err = container.Start()
|
||||||
|
if err != nil {
|
||||||
|
log.Println("ERROR start application problem: " + err.Error())
|
||||||
|
publish(app.Name, "backend problem", true)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
publish(app.Name, "started", false)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restart existing app
|
||||||
|
func restartEventHandler(m *nats.Msg, message *RequestMessage) error {
|
||||||
|
app, err := apps.Get(message.AppName)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("ERROR restart application problem: " + err.Error())
|
||||||
|
publish(app.Name, "backend problem", true)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
container := docker.Container{
|
||||||
|
App: app,
|
||||||
|
}
|
||||||
|
|
||||||
|
err = container.Restart()
|
||||||
|
if err != nil {
|
||||||
|
log.Println("ERROR restart application problem: " + err.Error())
|
||||||
|
publish(app.Name, "backend problem", true)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
publish(app.Name, "restarted", false)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copies body of the request into /srv/.ssh/authorized_keys
|
||||||
|
func updateKeysEventHandler(m *nats.Msg, message *RequestMessage) error {
|
||||||
|
err := waitForApp(message.AppName)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("ERROR enable tech problem: " + err.Error())
|
||||||
|
publish(message.AppName, "backend problem", true)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
body := message.Payload
|
||||||
|
|
||||||
|
app, err := apps.Get(message.AppName)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("ERROR keys update problem: " + err.Error())
|
||||||
|
publish(app.Name, "backend problem", true)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
container := docker.Container{
|
||||||
|
App: app,
|
||||||
|
}
|
||||||
|
|
||||||
|
err = container.SetFileContent(sshPubKeysLocation, body+"\n", "0600")
|
||||||
|
if err != nil {
|
||||||
|
log.Println("ERROR keys update problem: " + err.Error())
|
||||||
|
publish(app.Name, "backend problem", true)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
publish(app.Name, "keys updated", false)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set password for the app user in the container
|
||||||
|
func setPasswordEventHandler(m *nats.Msg, message *RequestMessage) error {
|
||||||
|
err := waitForApp(message.AppName)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("ERROR enable tech problem: " + err.Error())
|
||||||
|
publish(message.AppName, "backend problem", true)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
password := message.Payload
|
||||||
|
|
||||||
|
app, err := apps.Get(message.AppName)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("ERROR password update problem: " + err.Error())
|
||||||
|
publish(app.Name, "backend problem", true)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
container := docker.Container{
|
||||||
|
App: app,
|
||||||
|
}
|
||||||
|
|
||||||
|
err = container.SetPassword(password)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("ERROR password update problem: " + err.Error())
|
||||||
|
publish(app.Name, "backend problem", true)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
publish(app.Name, "password updated", false)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Application processes
|
||||||
|
func processesEventHandler(m *nats.Msg, message *RequestMessage) error {
|
||||||
|
app, err := apps.Get(message.AppName)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("ERROR processes list problem: " + err.Error())
|
||||||
|
publish(app.Name, "backend problem", true)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
container := docker.Container{
|
||||||
|
App: app,
|
||||||
|
}
|
||||||
|
|
||||||
|
processes, err := container.GetProcessList()
|
||||||
|
if err != nil {
|
||||||
|
log.Println("ERROR processes list problem: " + err.Error())
|
||||||
|
publish(app.Name, "backend problem", true)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
reply := ReplyMessage{
|
||||||
|
Payload: processes,
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := json.Marshal(reply)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("ERROR processes list problem: " + err.Error())
|
||||||
|
publish(app.Name, "backend problem", true)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = m.Respond(data)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("ERROR processes list problem: " + err.Error())
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enable one of the supported technologies or services (python, node, redis, ...)
|
||||||
|
func enableTechEventHandler(m *nats.Msg, message *RequestMessage) error {
|
||||||
|
service := message.Payload
|
||||||
|
|
||||||
|
err := waitForApp(message.AppName)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("ERROR enable tech problem: " + err.Error())
|
||||||
|
publish(message.AppName, "backend problem", true)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
app, err := apps.Get(message.AppName)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("ERROR enable tech problem: " + err.Error())
|
||||||
|
publish(app.Name, "backend problem", true)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
container := docker.Container{
|
||||||
|
App: app,
|
||||||
|
}
|
||||||
|
|
||||||
|
err = container.SetTechnology(service)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("ERROR enable tech problem: " + err.Error())
|
||||||
|
publish(app.Name, "backend problem", true)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
publish(app.Name, "tech updated", false)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rebuilds existing app, it keeps the data but creates the container again
|
||||||
|
func rebuildEventHandler(m *nats.Msg, message *RequestMessage) error {
|
||||||
|
app, err := apps.Get(message.AppName)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("ERROR rebuild app problem: " + err.Error())
|
||||||
|
publish(app.Name, "backend problem", true)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
|
log.Println("ERROR rebuild app problem: " + err.Error())
|
||||||
|
publish(app.Name, "backend problem", true)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = container.Create()
|
||||||
|
if err != nil {
|
||||||
|
log.Println("ERROR rebuild app problem: " + err.Error())
|
||||||
|
publish(app.Name, "backend problem", true)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = container.Start()
|
||||||
|
if err != nil {
|
||||||
|
log.Println("ERROR rebuild app problem: " + err.Error())
|
||||||
|
publish(app.Name, "backend problem", true)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
publish(app.Name, "app rebuild", false)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adds new label
|
||||||
|
func addLabelEventHandler(m *nats.Msg, message *RequestMessage) error {
|
||||||
|
label := message.Payload
|
||||||
|
|
||||||
|
err := apps.AddLabel(message.AppName, label)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("ERROR add label problem: " + err.Error())
|
||||||
|
publish(message.AppName, "backend problem", true)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
publish(message.AppName, "label added", false)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Removes existing label
|
||||||
|
func removeLabelEventHandler(m *nats.Msg, message *RequestMessage) error {
|
||||||
|
label := message.Payload
|
||||||
|
|
||||||
|
err := apps.RemoveLabel(message.AppName, label)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("ERROR remove label problem: " + err.Error())
|
||||||
|
publish(message.AppName, "backend problem", true)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
publish(message.AppName, "label removed", false)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Orphans returns directories in /srv that doesn't match any hosted application
|
||||||
|
func listOrphansEventHandler(m *nats.Msg, message *RequestMessage) error {
|
||||||
|
reply := ReplyMessage{
|
||||||
|
Error: true,
|
||||||
|
Payload: "not implemented yet",
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := json.Marshal(reply)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("ERROR orphans list problem: " + err.Error())
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = m.Respond(data)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("ERROR orphans list problem: " + err.Error())
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return info about the node including performance index
|
||||||
|
func getNoteEventHandler(m *nats.Msg, message *RequestMessage) error {
|
||||||
|
node, err := node.GetNodeInfo()
|
||||||
|
if err != nil {
|
||||||
|
log.Println("ERROR performance index problem: " + err.Error())
|
||||||
|
publish(message.AppName, "backend problem", true)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
reply := ReplyMessage{
|
||||||
|
Payload: node,
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := json.Marshal(reply)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("ERROR performance index problem: " + err.Error())
|
||||||
|
publish(message.AppName, "backend problem", true)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = m.Respond(data)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("ERROR performance index problem: " + err.Error())
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
33
main.go
33
main.go
@ -1,10 +1,12 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/labstack/echo"
|
"github.com/labstack/echo"
|
||||||
|
"github.com/nats-io/nats.go"
|
||||||
"github.com/rosti-cz/node-api/common"
|
"github.com/rosti-cz/node-api/common"
|
||||||
"github.com/rosti-cz/node-api/node"
|
"github.com/rosti-cz/node-api/node"
|
||||||
)
|
)
|
||||||
@ -12,7 +14,26 @@ import (
|
|||||||
// JSONIndent Indendation of JSON output format
|
// JSONIndent Indendation of JSON output format
|
||||||
const JSONIndent = " "
|
const JSONIndent = " "
|
||||||
|
|
||||||
|
var config common.Config
|
||||||
|
var nc *nats.Conn
|
||||||
|
|
||||||
|
func _init() {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
// Load config from environment variables
|
||||||
|
config = *common.GetConfig()
|
||||||
|
|
||||||
|
// Connect to the NATS service
|
||||||
|
nc, err = nats.Connect(config.NATSURL)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
_init()
|
||||||
|
defer nc.Drain()
|
||||||
|
|
||||||
// Close database at the end
|
// Close database at the end
|
||||||
db := common.GetDBConnection()
|
db := common.GetDBConnection()
|
||||||
defer db.Close()
|
defer db.Close()
|
||||||
@ -31,7 +52,7 @@ func main() {
|
|||||||
}
|
}
|
||||||
elapsed := time.Since(start)
|
elapsed := time.Since(start)
|
||||||
log.Printf("Stats gathering elapsed time: %.2fs\n", elapsed.Seconds())
|
log.Printf("Stats gathering elapsed time: %.2fs\n", elapsed.Seconds())
|
||||||
time.Sleep(30 * time.Second)
|
time.Sleep(300 * time.Second)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
@ -52,6 +73,13 @@ func main() {
|
|||||||
|
|
||||||
e.Use(TokenMiddleware)
|
e.Use(TokenMiddleware)
|
||||||
|
|
||||||
|
// NATS handling
|
||||||
|
// admin.apps.ALIAS.events
|
||||||
|
// admin.apps.ALIAS.states
|
||||||
|
subjectEvents := fmt.Sprintf("admin.apps.%s.requests", config.NATSAlias)
|
||||||
|
log.Println("> listening on " + subjectEvents)
|
||||||
|
nc.Subscribe(subjectEvents, messageHandler)
|
||||||
|
|
||||||
// UI
|
// UI
|
||||||
e.GET("/", homeHandler)
|
e.GET("/", homeHandler)
|
||||||
|
|
||||||
@ -89,9 +117,10 @@ func main() {
|
|||||||
// Copies body of the request into /srv/.ssh/authorized_keys
|
// Copies body of the request into /srv/.ssh/authorized_keys
|
||||||
e.PUT("/v1/apps/:name/keys", setKeysHandler)
|
e.PUT("/v1/apps/:name/keys", setKeysHandler)
|
||||||
|
|
||||||
|
// Enable one of the supported technologies or services (python, node, redis, ...)
|
||||||
e.PUT("/v1/apps/:name/set-services", setServicesHandler)
|
e.PUT("/v1/apps/:name/set-services", setServicesHandler)
|
||||||
|
|
||||||
// Rebuilds existing app, it keeps the data but created the container again
|
// Rebuilds existing app, it keeps the data but creates the container again
|
||||||
e.PUT("/v1/apps/:name/rebuild", rebuildAppHandler)
|
e.PUT("/v1/apps/:name/rebuild", rebuildAppHandler)
|
||||||
|
|
||||||
// Adds new label
|
// Adds new label
|
||||||
|
91
tools.go
Normal file
91
tools.go
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/nats-io/nats.go"
|
||||||
|
"github.com/rosti-cz/node-api/apps"
|
||||||
|
"github.com/rosti-cz/node-api/docker"
|
||||||
|
)
|
||||||
|
|
||||||
|
func errorReplyFormater(m *nats.Msg, message string, err error) error {
|
||||||
|
reply := ReplyMessage{
|
||||||
|
Error: true,
|
||||||
|
Payload: message,
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Println("ERROR:", err.Error())
|
||||||
|
|
||||||
|
data, err := json.Marshal(reply)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("ERROR:", err.Error())
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = m.Respond(data)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("ERROR:", err.Error())
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func publish(appName string, state string, isErr bool) {
|
||||||
|
stateMessage := StateMessage{
|
||||||
|
AppName: appName,
|
||||||
|
Error: isErr,
|
||||||
|
Message: state,
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := stateMessage.JSON()
|
||||||
|
if err != nil {
|
||||||
|
log.Println("ERROR: publish:", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
subjectEvents := fmt.Sprintf("admin.apps.%s.states", config.NATSAlias)
|
||||||
|
err = nc.Publish(subjectEvents, data)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("ERROR: publish:", err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// waitForApp waits until app is ready or timeout is reached.
|
||||||
|
// It's used in some async calls that need at least part of the
|
||||||
|
// environment prepared.
|
||||||
|
func waitForApp(appName string) error {
|
||||||
|
sleepFor := 5 * time.Second
|
||||||
|
loops := 6
|
||||||
|
|
||||||
|
for i := 0; i < loops; i++ {
|
||||||
|
err := updateState(appName)
|
||||||
|
if err != nil {
|
||||||
|
time.Sleep(sleepFor)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
app, err := apps.Get(appName)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
container := docker.Container{
|
||||||
|
App: app,
|
||||||
|
}
|
||||||
|
|
||||||
|
status, err := container.Status()
|
||||||
|
|
||||||
|
if status == "running" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
time.Sleep(sleepFor)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
return errors.New("timeout reached")
|
||||||
|
}
|
26
types.go
26
types.go
@ -1,8 +1,34 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
|
import "encoding/json"
|
||||||
|
|
||||||
// Path where authorized keys are
|
// Path where authorized keys are
|
||||||
const sshPubKeysLocation = "/srv/.ssh/authorized_keys"
|
const sshPubKeysLocation = "/srv/.ssh/authorized_keys"
|
||||||
|
|
||||||
|
// RequestMessage message
|
||||||
|
type RequestMessage struct {
|
||||||
|
AppName string `json:"name"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
Payload string `json:"payload"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type StateMessage struct {
|
||||||
|
AppName string `json:"name"` // Name of the related application stored in the main database
|
||||||
|
Error bool `json:"error"` // True if the message is an error message
|
||||||
|
Message string `json:"message"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StateMessage) JSON() ([]byte, error) {
|
||||||
|
return json.Marshal(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReplyMessage is returned as reply to a message that requires replying
|
||||||
|
type ReplyMessage struct {
|
||||||
|
AppName string `json:"name,omitempty"`
|
||||||
|
Error bool `json:"error,omitempty"` // True if this message is an error message and in that case Payload is string with an error message, otherwise it's standard response
|
||||||
|
Payload interface{} `json:"payload"`
|
||||||
|
}
|
||||||
|
|
||||||
// Message represents response with information about results of something
|
// Message represents response with information about results of something
|
||||||
type Message struct {
|
type Message struct {
|
||||||
// Message with different kind of information. Usually it's error message generated by dependencies or stdlib or simply ok.
|
// Message with different kind of information. Usually it's error message generated by dependencies or stdlib or simply ok.
|
||||||
|
Loading…
Reference in New Issue
Block a user