Added Edgeapps Options Feature

pull/30/head
= 2023-10-29 20:48:14 +01:00
parent 445bc41d0e
commit ca7f4af7fe
4 changed files with 254 additions and 2 deletions

View File

@ -6,7 +6,7 @@ import (
"os" "os"
"strings" "strings"
"time" "time"
"github.com/joho/godotenv" "github.com/joho/godotenv"
"github.com/edgebox-iot/edgeboxctl/internal/system" "github.com/edgebox-iot/edgeboxctl/internal/system"
@ -23,6 +23,8 @@ type EdgeApp struct {
InternetAccessible bool `json:"internet_accessible"` InternetAccessible bool `json:"internet_accessible"`
NetworkURL string `json:"network_url"` NetworkURL string `json:"network_url"`
InternetURL string `json:"internet_url"` InternetURL string `json:"internet_url"`
Options []EdgeAppOption `json:"options"`
NeedsConfig bool `json:"needs_config"`
} }
// MaybeEdgeApp : Boolean flag for validation of edgeapp existance // MaybeEdgeApp : Boolean flag for validation of edgeapp existance
@ -43,8 +45,20 @@ type EdgeAppService struct {
IsRunning bool `json:"is_running"` IsRunning bool `json:"is_running"`
} }
type EdgeAppOption struct {
Key string `json:"key"`
Value string `json:"value"`
DefaultValue string `json:"default_value"`
Format string `json:"format"`
Description string `json:"description"`
IsSecret bool `json:"is_secret"`
IsInstallLocked bool `json:"is_install_locked"`
}
const configFilename = "/edgebox-compose.yml" const configFilename = "/edgebox-compose.yml"
const envFilename = "/edgebox.env" const envFilename = "/edgebox.env"
const optionsTemplateFilename = "/edgeapp.template.env"
const optionsEnvFilename = "/edgeapp.env"
const runnableFilename = "/.run" const runnableFilename = "/.run"
const myEdgeAppServiceEnvFilename = "/myedgeapp.env" const myEdgeAppServiceEnvFilename = "/myedgeapp.env"
const defaultContainerOperationSleepTime time.Duration = time.Second * 10 const defaultContainerOperationSleepTime time.Duration = time.Second * 10
@ -63,6 +77,7 @@ func GetEdgeApp(ID string) MaybeEdgeApp {
edgeAppName := ID edgeAppName := ID
edgeAppDescription := "" edgeAppDescription := ""
edgeAppOptions := []EdgeAppOption{}
edgeAppEnv, err := godotenv.Read(utils.GetPath(utils.EdgeAppsPath) + ID + envFilename) edgeAppEnv, err := godotenv.Read(utils.GetPath(utils.EdgeAppsPath) + ID + envFilename)
@ -77,6 +92,103 @@ func GetEdgeApp(ID string) MaybeEdgeApp {
} }
} }
needsConfig := false
hasFilledOptions := false
edgeAppOptionsTemplate, err := godotenv.Read(utils.GetPath(utils.EdgeAppsPath) + ID + optionsTemplateFilename)
if err != nil {
log.Println("Error loading options template file for edgeapp " + edgeAppName)
} else {
// Try to read the edgeAppOptionsEnv file
edgeAppOptionsEnv, err := godotenv.Read(utils.GetPath(utils.EdgeAppsPath) + ID + optionsEnvFilename)
if err != nil {
log.Println("Error loading options env file for edgeapp " + edgeAppName)
} else {
hasFilledOptions = true
}
for key, value := range edgeAppOptionsTemplate {
optionFilledValue := ""
if hasFilledOptions {
// Check if key exists in edgeAppOptionsEnv
optionFilledValue = edgeAppOptionsEnv[key]
}
format := ""
defaultValue := ""
description := ""
installLocked := false
// Parse value to separate by | and get the format, installLocked, description and default value
// Format is the first element
// InstallLocked is the second element
// Description is the third element
// Default value is the fourth element
valueSlices := strings.Split(value, "|")
if len(valueSlices) > 0 {
format = valueSlices[0]
}
if len(valueSlices) > 1 {
installLocked = valueSlices[1] == "true"
}
if len(valueSlices) > 2 {
description = valueSlices[2]
}
if len(valueSlices) > 3 {
defaultValue = valueSlices[3]
}
// // If value contains ">|", then get everything that is to the right of it as the description
// // and get everything between "<>" as the format
// if strings.Contains(value, ">|") {
// description = strings.Split(value, ">|")[1]
// // Check if description has default value. That would be everything that is to the right of the last "|"
// if strings.Contains(description, "|") {
// defaultValue = strings.Split(description, "|")[1]
// description = strings.Split(description, "|")[0]
// }
// value = strings.Split(value, ">|")[0]
// // Remove the initial < from value
// value = strings.TrimPrefix(value, "<")
// } else {
// // Trim initial < and final > from value
// value = strings.TrimPrefix(value, "<")
// value = strings.TrimSuffix(value, ">")
// }
isSecret := false
// Check if the lowercased key string contains the letters "pass", "secret", "key"
lowercaseKey := strings.ToLower(key)
// check if lowercaseInput contains "pass", "key", or "secret", or "token"
if strings.Contains(lowercaseKey, "pass") ||
strings.Contains(lowercaseKey, "key") ||
strings.Contains(lowercaseKey, "secret") ||
strings.Contains(lowercaseKey, "token") {
isSecret = true
}
currentOption := EdgeAppOption{
Key: key,
Value: optionFilledValue,
DefaultValue: defaultValue,
Description: description,
Format: format,
IsSecret: isSecret,
IsInstallLocked: installLocked,
}
edgeAppOptions = append(edgeAppOptions, currentOption)
if optionFilledValue == "" {
needsConfig = true
}
}
}
edgeAppInternetAccessible := false edgeAppInternetAccessible := false
edgeAppInternetURL := "" edgeAppInternetURL := ""
@ -100,6 +212,8 @@ func GetEdgeApp(ID string) MaybeEdgeApp {
InternetAccessible: edgeAppInternetAccessible, InternetAccessible: edgeAppInternetAccessible,
NetworkURL: ID + "." + system.GetHostname() + ".local", NetworkURL: ID + "." + system.GetHostname() + ".local",
InternetURL: edgeAppInternetURL, InternetURL: edgeAppInternetURL,
Options: edgeAppOptions,
NeedsConfig: needsConfig,
}, },
Valid: true, Valid: true,
} }

View File

@ -100,6 +100,29 @@ func SetupCloudOptions() {
utils.Exec("/", "rm", []string{cloudEnvFileLocationPath}) utils.Exec("/", "rm", []string{cloudEnvFileLocationPath})
} }
func StartSystemLogger() {
fmt.Println("Starting system logger")
loggerPath := utils.GetPath(utils.LoggerPath)
utils.Exec(loggerPath, "make", []string{"start"})
}
// UpdateSystemLoggerServices: Updates the services.txt file with the services that are currently running
func UpdateSystemLoggerServices(services []string) {
fmt.Println("Updating system logger services:")
fmt.Println(services)
loggerPath := utils.GetPath(utils.LoggerPath)
utils.Exec(loggerPath, "bash", []string{"-c", "rm services.txt && touch services.txt"})
for _, service := range services {
fmt.Println("Adding " + service + " to services.txt")
utils.Exec(loggerPath, "bash", []string{"-c", "echo " + service + " >> services.txt"})
}
// Add empty line at the end of file (best practice)
utils.Exec(loggerPath, "bash", []string{"-c", "echo '' >> services.txt"})
}
// StartWs: Starts the webserver service for Edgeapps // StartWs: Starts the webserver service for Edgeapps
func StartWs() { func StartWs() {
wsPath := utils.GetPath(utils.WsPath) wsPath := utils.GetPath(utils.WsPath)

View File

@ -33,6 +33,12 @@ type Task struct {
Updated string `json:"updated"` Updated string `json:"updated"`
} }
// TaskOption: Struct for Task Options (kv pair)
type TaskOption struct {
Key string `json:"key"`
Value string `json:"value"`
}
type taskSetupTunnelArgs struct { type taskSetupTunnelArgs struct {
DomainName string `json:"domain_name"` DomainName string `json:"domain_name"`
} }
@ -53,6 +59,12 @@ type taskStopEdgeAppArgs struct {
ID string `json:"id"` ID string `json:"id"`
} }
type taskSetEdgeAppOptionsArgs struct {
ID string `json:"id"`
// Options should be an array of "key":"value" pairs
Options []TaskOption `json:"options"`
}
type taskEnableOnlineArgs struct { type taskEnableOnlineArgs struct {
ID string `json:"id"` ID string `json:"id"`
InternetURL string `json:"internet_url"` InternetURL string `json:"internet_url"`
@ -262,6 +274,19 @@ func ExecuteTask(task Task) Task {
task.Result = sql.NullString{String: taskResult, Valid: true} task.Result = sql.NullString{String: taskResult, Valid: true}
} }
case "set_edgeapp_options":
log.Println("Setting EdgeApp Options...")
var args taskSetEdgeAppOptionsArgs
// {"id":"podgrab","options":{"PODGRAB_PASSWORD":"fumarmata"}}
err := json.Unmarshal([]byte(task.Args.String), &args)
if err != nil {
log.Printf("Error reading arguments of set_edgeapp_options task: %s", err)
} else {
taskResult := taskSetEdgeAppOptions(args)
task.Result = sql.NullString{String: taskResult, Valid: true}
}
case "enable_online": case "enable_online":
log.Println("Enabling online access to EdgeApp...") log.Println("Enabling online access to EdgeApp...")
@ -376,7 +401,7 @@ func ExecuteSchedules(tick int) {
log.Println(taskGetStorageDevices()) log.Println(taskGetStorageDevices())
taskStartWs() taskStartWs()
log.Println(taskGetEdgeApps()) log.Println(taskGetEdgeApps())
taskUpdateSystemLoggerServices()
} }
if tick%5 == 0 { if tick%5 == 0 {
@ -388,6 +413,7 @@ func ExecuteSchedules(tick int) {
if tick%30 == 0 { if tick%30 == 0 {
// Executing every 30 ticks // Executing every 30 ticks
log.Println(taskGetEdgeApps()) log.Println(taskGetEdgeApps())
taskUpdateSystemLoggerServices()
// RESET SOME VARIABLES HERE IF NEEDED, SINCE SYSTEM IS UNBLOCKED // RESET SOME VARIABLES HERE IF NEEDED, SINCE SYSTEM IS UNBLOCKED
utils.WriteOption("BACKUP_IS_WORKING", "0") utils.WriteOption("BACKUP_IS_WORKING", "0")
@ -909,6 +935,57 @@ func taskStopEdgeApp(args taskStopEdgeAppArgs) string {
return string(resultJSON) return string(resultJSON)
} }
func taskSetEdgeAppOptions(args taskSetEdgeAppOptionsArgs) string {
// Id is the edgeapp id
appID := args.ID
// Open the file to write the options,
// it is an env file in /home/system/components/apps/<app_id>/edgeapp.env
// Get the path to the edgeapp.env file
edgeappEnvPath := "/home/system/components/apps/" + appID + "/edgeapp.env"
// If the file does not exist, create it
if _, err := os.Stat(edgeappEnvPath); os.IsNotExist(err) {
// Create the file
_, err := os.Create(edgeappEnvPath)
if err != nil {
log.Printf("Error creating edgeapp.env file: %s", err)
}
}
// It is an env file, so we can use go-dotenv to write the options
// Open the file
edgeappEnvFile, err := os.OpenFile(edgeappEnvPath, os.O_WRONLY, 0600)
if err != nil {
log.Printf("Error opening edgeapp.env file: %s", err)
}
// Write the options to the file
for _, value := range args.Options {
// Write the option to the file
_, err := edgeappEnvFile.WriteString(value.Key + "=" + value.Value + "\n")
if err != nil {
log.Printf("Error writing option to edgeapp.env file: %s", err)
}
}
// Close the file
err = edgeappEnvFile.Close()
if err != nil {
log.Printf("Error closing edgeapp.env file: %s", err)
}
result := edgeapps.GetEdgeAppStatus(appID)
resultJSON, _ := json.Marshal(result)
system.StartWs()
taskGetEdgeApps() // This task will imediatelly update the entry in the api database.
return string(resultJSON)
}
func taskEnableOnline(args taskEnableOnlineArgs) string { func taskEnableOnline(args taskEnableOnlineArgs) string {
fmt.Println("Executing taskEnableOnline for " + args.ID) fmt.Println("Executing taskEnableOnline for " + args.ID)
@ -961,6 +1038,35 @@ func taskSetReleaseVersion() string {
return diagnostics.Version return diagnostics.Version
} }
func taskUpdateSystemLoggerServices() string {
fmt.Println("Executing taskUpdateSystemLoggerServices")
// The input is an array of strings
// Each string is a service name to be logged
var input []string
// Get the services
edgeAppsList := utils.ReadOption("EDGEAPPS_LIST")
var edgeApps []edgeapps.EdgeApp
err := json.Unmarshal([]byte(edgeAppsList), &edgeApps)
if err != nil {
log.Fatalf("failed to unmarshal EDGEAPPS_LIST: %v", err)
}
for _, edgeApp := range edgeApps {
for _, service := range edgeApp.Services {
input = append(input, service.ID)
}
}
input = append(input, "edgeboxctl")
input = append(input, "tunnel")
// Run the system logger
system.UpdateSystemLoggerServices(input)
return "{\"status\": \"ok\"}"
}
func taskGetEdgeApps() string { func taskGetEdgeApps() string {
fmt.Println("Executing taskGetEdgeApps") fmt.Println("Executing taskGetEdgeApps")

View File

@ -111,6 +111,8 @@ const ApiPath string = "apiPath"
const EdgeAppsPath string = "edgeAppsPath" const EdgeAppsPath string = "edgeAppsPath"
const EdgeAppsBackupPath string = "edgeAppsBackupPath" const EdgeAppsBackupPath string = "edgeAppsBackupPath"
const WsPath string = "wsPath" const WsPath string = "wsPath"
const LoggerPath string = "loggerPath"
// GetPath : Returns either the hardcoded path, or a overwritten value via .env file at project root. Register paths here for seamless working code between dev and prod environments ;) // GetPath : Returns either the hardcoded path, or a overwritten value via .env file at project root. Register paths here for seamless working code between dev and prod environments ;)
func GetPath(pathKey string) string { func GetPath(pathKey string) string {
@ -172,6 +174,13 @@ func GetPath(pathKey string) string {
targetPath = "/home/system/components/ws/" targetPath = "/home/system/components/ws/"
} }
case LoggerPath:
if env["LOGGER_PATH"] != "" {
targetPath = env["LOGGER_PATH"]
} else {
targetPath = "/home/system/components/logger/"
}
case BackupPasswordFileLocation: case BackupPasswordFileLocation:
if env["BACKUP_PASSWORD_FILE_LOCATION"] != "" { if env["BACKUP_PASSWORD_FILE_LOCATION"] != "" {