Adam Štrauch
fdcdca237f
All checks were successful
continuous-integration/drone/push Build is passing
Docker client upgrade, Miner detection Flags for apps
1131 lines
29 KiB
Go
1131 lines
29 KiB
Go
package main
|
|
|
|
/*
|
|
Message handling is pretty straightforward. When request is received it's
|
|
usually asynchronic and the client is not waiting for an reply. In this case
|
|
client writes down that something is happening here and when it's done publish()
|
|
function is used to notify everybody that it's done. Client usually listens to
|
|
this channel and it finish his part as it needs.
|
|
|
|
In case there is a reply we use NATS's responde() method to return the data
|
|
as soon as possible.
|
|
*/
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"log"
|
|
"regexp"
|
|
"strings"
|
|
|
|
"github.com/nats-io/nats.go"
|
|
"github.com/pkg/errors"
|
|
"github.com/rosti-cz/node-api/apps"
|
|
"github.com/rosti-cz/node-api/common"
|
|
docker "github.com/rosti-cz/node-api/containers"
|
|
"github.com/rosti-cz/node-api/detector"
|
|
"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,
|
|
"register": registerEventHandler,
|
|
"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,
|
|
"create_snapshot": createSnapshotEventHandler,
|
|
"restore_from_snapshot": restoreFromSnapshotEventHandler,
|
|
"list_snapshots": listSnapshotsEventHandler,
|
|
"list_apps_snapshots": listAppsSnapshotsEventHandler,
|
|
"list_snapshots_by_label": listSnapshotsByLabelEventHandler,
|
|
"get_snapshot": getSnapshotEventHandler,
|
|
"get_snapshot_download_link": getSnapshotDownloadLinkEventHandler,
|
|
"delete_snapshot": deleteSnapshotEventHandler,
|
|
"delete_app_snapshots": deleteAppSnapshotsEventHandler,
|
|
}
|
|
|
|
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
|
|
|
|
// 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)
|
|
}
|
|
|
|
processor := apps.AppsProcessor{
|
|
DB: common.GetDBConnection(),
|
|
}
|
|
processor.Init()
|
|
applications, err := processor.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)
|
|
}
|
|
|
|
processor := apps.AppsProcessor{
|
|
DB: common.GetDBConnection(),
|
|
}
|
|
app, err := processor.Get(message.AppName)
|
|
if err != nil {
|
|
return errorReplyFormater(m, "backend error", err)
|
|
}
|
|
|
|
// Gather runtime info about the container
|
|
container := docker.Container{
|
|
App: app,
|
|
}
|
|
|
|
status, err := container.Status()
|
|
if err != nil {
|
|
log.Printf("backend error: %v\n", err)
|
|
return errorReplyFormater(m, "backend error", err)
|
|
}
|
|
if status == "running" {
|
|
var err error
|
|
app.Techs, err = container.GetTechs()
|
|
if err != nil {
|
|
log.Printf("backend error: %v\n", err)
|
|
return errorReplyFormater(m, "backend error", err)
|
|
}
|
|
app.PrimaryTech, err = container.GetPrimaryTech()
|
|
if err != nil {
|
|
log.Printf("backend error: %v\n", err)
|
|
return errorReplyFormater(m, "backend error", err)
|
|
}
|
|
|
|
processList, err := container.GetSystemProcesses()
|
|
if err != nil {
|
|
log.Printf("backend error: %v\n", err)
|
|
return errorReplyFormater(m, "backend error", err)
|
|
}
|
|
|
|
flags, err := detector.Check(processList)
|
|
if err != nil {
|
|
log.Printf("backend error: %v\n", err)
|
|
return errorReplyFormater(m, "backend error", err)
|
|
}
|
|
app.Flags = flags.String()
|
|
}
|
|
|
|
// Assembling reply message
|
|
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 (unmarshal): " + err.Error())
|
|
publish(message.AppName, "payload parsing problem", true)
|
|
return err
|
|
}
|
|
|
|
processor := apps.AppsProcessor{
|
|
DB: common.GetDBConnection(),
|
|
}
|
|
err = processor.New(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 create application problem (validation problem): " + validationError.Error())
|
|
publish(message.AppName, "validation problem", true)
|
|
return err
|
|
}
|
|
log.Println("ERROR create application problem (processor.New): " + err.Error())
|
|
publish(message.AppName, "backend problem", true)
|
|
return err
|
|
}
|
|
|
|
container := docker.Container{
|
|
App: &appTemplate,
|
|
}
|
|
|
|
err = container.Create()
|
|
if err != nil {
|
|
log.Println("ERROR create application problem (container.Create): " + err.Error())
|
|
publish(message.AppName, "backend problem", true)
|
|
return err
|
|
}
|
|
|
|
// Restore from snapshot if it's noted in the request
|
|
if len(appTemplate.Snapshot) > 0 {
|
|
log.Printf("App %s is going to be created from %s snapshot\n", message.AppName, appTemplate.Snapshot)
|
|
|
|
// Restore the data
|
|
err = snapshotProcessor.RestoreSnapshot(appTemplate.Snapshot, message.AppName)
|
|
if err != nil {
|
|
log.Println("ERROR restore snapshot error: " + err.Error())
|
|
publish(message.AppName, "backend problem", true)
|
|
return err
|
|
}
|
|
}
|
|
|
|
err = container.Start()
|
|
if err != nil {
|
|
log.Println("ERROR create application problem (container.Start): " + err.Error())
|
|
publish(message.AppName, "backend problem", true)
|
|
return err
|
|
}
|
|
|
|
publish(message.AppName, "created", false)
|
|
|
|
return nil
|
|
}
|
|
|
|
// Registers new app without creating an container
|
|
func registerEventHandler(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 (Unmarshal): " + err.Error())
|
|
publish(message.AppName, "payload parsing problem", true)
|
|
return err
|
|
}
|
|
|
|
processor := apps.AppsProcessor{
|
|
DB: common.GetDBConnection(),
|
|
}
|
|
err = processor.New(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 create application problem (validation): " + validationError.Error())
|
|
publish(message.AppName, "validation problem", true)
|
|
return err
|
|
}
|
|
log.Println("ERROR create application problem (processor.New): " + err.Error())
|
|
publish(message.AppName, "backend problem", true)
|
|
return err
|
|
}
|
|
|
|
publish(message.AppName, "registered", 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 (unmarshal): " + err.Error())
|
|
publish(message.AppName, "payload parsing problem", true)
|
|
return err
|
|
}
|
|
|
|
processor := apps.AppsProcessor{
|
|
DB: common.GetDBConnection(),
|
|
}
|
|
app, err := processor.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(message.AppName, "backend problem", true)
|
|
return err
|
|
}
|
|
log.Println("ERROR update application problem: " + err.Error())
|
|
publish(message.AppName, "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(app.Name, "backend problem", true)
|
|
return err
|
|
}
|
|
|
|
err = container.Create()
|
|
if err != nil {
|
|
log.Println("ERROR update application problem: " + err.Error())
|
|
publish(app.Name, "backend problem", true)
|
|
return err
|
|
}
|
|
|
|
err = container.Start()
|
|
if err != nil {
|
|
log.Println("ERROR update application problem: " + err.Error())
|
|
publish(app.Name, "backend problem", true)
|
|
return err
|
|
}
|
|
|
|
publish(app.Name, "updated", false)
|
|
return nil
|
|
}
|
|
|
|
// Delete one app
|
|
func deleteEventHandler(m *nats.Msg, message *RequestMessage) error {
|
|
processor := apps.AppsProcessor{
|
|
DB: common.GetDBConnection(),
|
|
}
|
|
app, err := processor.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 = processor.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 {
|
|
processor := apps.AppsProcessor{
|
|
DB: common.GetDBConnection(),
|
|
}
|
|
app, err := processor.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 {
|
|
processor := apps.AppsProcessor{
|
|
DB: common.GetDBConnection(),
|
|
}
|
|
app, err := processor.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 {
|
|
processor := apps.AppsProcessor{
|
|
DB: common.GetDBConnection(),
|
|
}
|
|
app, err := processor.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
|
|
|
|
processor := apps.AppsProcessor{
|
|
DB: common.GetDBConnection(),
|
|
}
|
|
app, err := processor.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
|
|
|
|
processor := apps.AppsProcessor{
|
|
DB: common.GetDBConnection(),
|
|
}
|
|
app, err := processor.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 {
|
|
processor := apps.AppsProcessor{
|
|
DB: common.GetDBConnection(),
|
|
}
|
|
app, err := processor.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, ...)
|
|
// If payload contains only name of the tech the default version for given image is selected.
|
|
// Otherwise it can be passed in format tech:version.
|
|
func enableTechEventHandler(m *nats.Msg, message *RequestMessage) error {
|
|
matched, err := regexp.Match(`[a-z0-9A-Z]*:?[0-9\.\-]*`, []byte(message.Payload))
|
|
if err != nil {
|
|
log.Println("ERROR enable tech problem: " + err.Error())
|
|
publish(message.AppName, "backend problem", true)
|
|
return err
|
|
}
|
|
if !matched {
|
|
return errors.New("payload malformation, it has to be in format tech:version")
|
|
}
|
|
|
|
service := message.Payload
|
|
version := ""
|
|
|
|
if strings.Contains(service, ":") {
|
|
parts := strings.SplitN(message.Payload, ":", 2)
|
|
if len(parts) != 2 {
|
|
return errors.New("service and version malformat")
|
|
}
|
|
|
|
service = parts[0]
|
|
version = parts[1]
|
|
}
|
|
|
|
err = waitForApp(message.AppName)
|
|
if err != nil {
|
|
log.Println("ERROR enable tech problem: " + err.Error())
|
|
publish(message.AppName, "backend problem", true)
|
|
return err
|
|
}
|
|
|
|
processor := apps.AppsProcessor{
|
|
DB: common.GetDBConnection(),
|
|
}
|
|
app, err := processor.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, version)
|
|
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 {
|
|
processor := apps.AppsProcessor{
|
|
DB: common.GetDBConnection(),
|
|
}
|
|
app, err := processor.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
|
|
|
|
processor := apps.AppsProcessor{
|
|
DB: common.GetDBConnection(),
|
|
}
|
|
err := processor.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
|
|
|
|
processor := apps.AppsProcessor{
|
|
DB: common.GetDBConnection(),
|
|
}
|
|
err := processor.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
|
|
}
|
|
|
|
/*
|
|
getNoteEventHandler returns 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
|
|
}
|
|
|
|
/*
|
|
createSnapshotEventHandler create snapshot of given application
|
|
|
|
Uses appName from the message struct
|
|
|
|
Payload: no payload needed
|
|
Response: notification when it's done or error
|
|
*/
|
|
func createSnapshotEventHandler(m *nats.Msg, message *RequestMessage) error {
|
|
_, err := snapshotProcessor.CreateSnapshot(message.AppName, strings.Split(message.Payload, ","))
|
|
if err != nil {
|
|
log.Println("ERROR create snapshot error: " + err.Error())
|
|
publish(message.AppName, "backend problem", true)
|
|
return err
|
|
}
|
|
|
|
publish(message.AppName, "snapshot created", false)
|
|
|
|
return nil
|
|
}
|
|
|
|
/*
|
|
restoreFromSnapshotEventHandler restores app from a snapshot. The app has to exist
|
|
before it can be restored. Any snapshot can be used to restore any application.
|
|
Use labels to store information about what app should be created.
|
|
|
|
Uses appName from the message struct as the destination app where the data should
|
|
be restored
|
|
|
|
Payload: string with the snapshot name
|
|
Response: notification when it's done or error
|
|
*/
|
|
func restoreFromSnapshotEventHandler(m *nats.Msg, message *RequestMessage) error {
|
|
processor := apps.AppsProcessor{
|
|
DB: common.GetDBConnection(),
|
|
}
|
|
app, err := processor.Get(message.AppName)
|
|
if err != nil {
|
|
log.Println("ERROR restore snapshot problem: " + err.Error())
|
|
publish(app.Name, "backend problem", true)
|
|
return err
|
|
}
|
|
|
|
container := docker.Container{
|
|
App: app,
|
|
}
|
|
|
|
// Stop the container
|
|
status, err := container.Status()
|
|
if err != nil {
|
|
log.Println("ERROR restore snapshot 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 restore snapshot problem: " + err.Error())
|
|
publish(app.Name, "backend problem", true)
|
|
return err
|
|
}
|
|
}
|
|
|
|
// Restore the data
|
|
err = snapshotProcessor.RestoreSnapshot(message.Payload, message.AppName)
|
|
if err != nil {
|
|
log.Println("ERROR restore snapshot error: " + err.Error())
|
|
publish(message.AppName, "backend problem", true)
|
|
return err
|
|
}
|
|
|
|
// Start the container
|
|
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
|
|
}
|
|
|
|
// notify clients that it's all done
|
|
publish(message.AppName, "snapshot restored", false)
|
|
|
|
return nil
|
|
}
|
|
|
|
/*
|
|
listSnapshotsEventHandler returns list of snapshots related to a single application
|
|
|
|
Uses appName from the message
|
|
|
|
Payload: no payload needed
|
|
Response: replies with list of snapshots or an error message
|
|
*/
|
|
func listSnapshotsEventHandler(m *nats.Msg, message *RequestMessage) error {
|
|
snapshots, err := snapshotProcessor.ListAppSnapshots(message.AppName)
|
|
if err != nil {
|
|
return errorReplyFormater(m, "backend error", err)
|
|
}
|
|
|
|
var output SnapshotsMetadata
|
|
for _, snapshot := range snapshots {
|
|
output = append(output, SnapshotMetadata{
|
|
Key: snapshot.KeyName(snapshotProcessor.IndexLabel),
|
|
Metadata: snapshot,
|
|
})
|
|
}
|
|
|
|
reply := ReplyMessage{
|
|
Payload: output,
|
|
}
|
|
|
|
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 of snapshots:", err.Error())
|
|
}
|
|
return nil
|
|
}
|
|
|
|
/*
|
|
listAppsSnapshotsEventHandler returns list of snapshots related to list of application names.
|
|
|
|
Payload: list of appNames separated by comma (no spaces)
|
|
Response: replies with list of snapshots or an error message
|
|
*/
|
|
func listAppsSnapshotsEventHandler(m *nats.Msg, message *RequestMessage) error {
|
|
snapshots, err := snapshotProcessor.ListAppsSnapshots(strings.Split(message.Payload, ","))
|
|
if err != nil {
|
|
return errorReplyFormater(m, "backend error", err)
|
|
}
|
|
|
|
var output SnapshotsMetadata
|
|
for _, snapshot := range snapshots {
|
|
output = append(output, SnapshotMetadata{
|
|
Key: snapshot.KeyName(snapshotProcessor.IndexLabel),
|
|
Metadata: snapshot,
|
|
})
|
|
}
|
|
|
|
reply := ReplyMessage{
|
|
Payload: output,
|
|
}
|
|
|
|
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 of snapshots:", err.Error())
|
|
}
|
|
return nil
|
|
}
|
|
|
|
/*
|
|
listSnapshotsByLabelEventHandler returns list of snapshots with given label
|
|
|
|
Payload: snapshot label
|
|
Response: replies with list of snapshots or an error message
|
|
*/
|
|
func listSnapshotsByLabelEventHandler(m *nats.Msg, message *RequestMessage) error {
|
|
snapshots, err := snapshotProcessor.ListAppsSnapshotsByLabel(message.Payload)
|
|
if err != nil {
|
|
return errorReplyFormater(m, "backend error", err)
|
|
}
|
|
|
|
var output SnapshotsMetadata
|
|
for _, snapshot := range snapshots {
|
|
output = append(output, SnapshotMetadata{
|
|
Key: snapshot.KeyName(snapshotProcessor.IndexLabel),
|
|
Metadata: snapshot,
|
|
})
|
|
}
|
|
|
|
reply := ReplyMessage{
|
|
Payload: output,
|
|
}
|
|
|
|
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 of snapshots:", err.Error())
|
|
}
|
|
return nil
|
|
}
|
|
|
|
/*
|
|
getSnapshotEventHandler returns a single snapshot for given key from the request payload.
|
|
|
|
Payload: snapshot's key
|
|
Response: snapshot metadata
|
|
*/
|
|
func getSnapshotEventHandler(m *nats.Msg, message *RequestMessage) error {
|
|
snapshot, err := snapshotProcessor.GetSnapshot(message.Payload)
|
|
if err != nil {
|
|
return errorReplyFormater(m, "backend error", err)
|
|
}
|
|
output := SnapshotMetadata{
|
|
Key: snapshot.KeyName(snapshotProcessor.IndexLabel),
|
|
Metadata: snapshot,
|
|
}
|
|
|
|
data, err := json.Marshal(output)
|
|
if err != nil {
|
|
return errorReplyFormater(m, "reply formatter error", err)
|
|
}
|
|
|
|
err = m.Respond(data)
|
|
if err != nil {
|
|
log.Println("ERROR: get snapshot:", err.Error())
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
/*
|
|
getSnapshotDownloadLinkEventHandler return URL that can be used to download snapshot
|
|
|
|
Payload: string with a snapshot name (key)
|
|
Response: string with the URL
|
|
*/
|
|
func getSnapshotDownloadLinkEventHandler(m *nats.Msg, message *RequestMessage) error {
|
|
link, err := snapshotProcessor.GetDownloadLink(message.Payload)
|
|
if err != nil {
|
|
return errorReplyFormater(m, "backend error", err)
|
|
}
|
|
|
|
reply := ReplyMessage{
|
|
Payload: link,
|
|
}
|
|
|
|
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 snapshot:", err.Error())
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
/*
|
|
deleteSnapshotEventHandler delete a single snapshot. This is not bound to application name.
|
|
|
|
Payload: string with a snapshot name
|
|
Response: notification when it's done or error
|
|
*/
|
|
func deleteSnapshotEventHandler(m *nats.Msg, message *RequestMessage) error {
|
|
err := snapshotProcessor.DeleteSnapshot(message.Payload)
|
|
if err != nil {
|
|
return errorReplyFormater(m, "backend error", err)
|
|
}
|
|
|
|
publish(message.AppName, "snapshot deleted", false)
|
|
|
|
return nil
|
|
}
|
|
|
|
/*
|
|
deleteAppSnapshotsEventHandler deletes all snapshots related to a single application
|
|
|
|
Uses appName from the message struct
|
|
|
|
Payload: no payload needed
|
|
Response: notification when it's done or error
|
|
*/
|
|
func deleteAppSnapshotsEventHandler(m *nats.Msg, message *RequestMessage) error {
|
|
err := snapshotProcessor.DeleteAppSnapshots(message.AppName)
|
|
if err != nil {
|
|
return errorReplyFormater(m, "backend error", err)
|
|
}
|
|
|
|
publish(message.AppName, "snapshots deleted", false)
|
|
|
|
return nil
|
|
}
|