15 Commits
Author SHA1 Message Date
Paulo Truta 7fd9c94c7b Explicit arch var for cloud release make command 2022-10-08 16:20:37 +00:00
Paulo TrutaandMalachi Soord b6bf06172c Update internal/utils/utils.go
Thanks @inverse !

Co-authored-by: Malachi Soord <[email protected]>
2022-02-14 21:33:29 +01:00
Paulo TrutaandMalachi Soord a33a6f5e84 Update internal/utils/utils.go
Thanks @inverse !

Co-authored-by: Malachi Soord <[email protected]>
2022-02-14 21:33:10 +01:00
Paulo Truta 01dc4943b1 Various polishments, now validating that ws is ready to run for every command 2022-02-14 17:23:08 +00:00
Paulo Truta ac9376686c Merge branch 'cli-support' of https://github.com/edgebox-iot/edgeboxctl into cli-support 2022-02-13 14:40:10 +00:00
Paulo Truta 09f07c01db Fixed shorthand command and fixed incomplete func comment 2022-02-13 14:40:04 +00:00
Paulo TrutaandMalachi Soord 23f9926b4b Update cmd/edgeboxctl/main.go
Co-authored-by: Malachi Soord <[email protected]>
2022-02-12 13:36:35 +01:00
Paulo Truta 9eb47f3248 Implemented list command + task. colorjson output disabled for this command (bug detected) 2022-02-09 23:27:33 +00:00
Paulo Truta a2492ef59c Implemented command for tunnel setup and stubs for enabling and disabling the tunnel# 2022-02-09 23:06:50 +00:00
Paulo Truta 1427a023fd Reworked command route handler logic, fixed arguments for enable_online commands 2022-02-09 22:53:15 +00:00
Paulo Truta a44c44baa8 Added validation for argument in start/stop app command. Colorized error output on validation fail 2022-02-07 20:39:53 +00:00
Paulo Truta 9ccb3f1d71 Commented out unecessary logging (should be under --verbose flag), tidy up command logic, implemented stop and start app commands 2022-02-07 20:20:09 +00:00
Paulo Truta 6a61ac4d17 Using fmt.Sprintf instead of casting int to string 2022-02-06 17:25:41 +00:00
Paulo Truta c88726917f Minor adjustments, Example poc for sub commands with 'app start' and 'app stop' working concepts 2022-02-06 17:10:02 +00:00
Paulo Truta 2f36538e1e Installed urfave/cli/v2 lib and cleaned main(). Runs as service clock by default 2022-02-06 15:15:32 +00:00
24 changed files with 535 additions and 2960 deletions
-10
View File
@@ -1,10 +0,0 @@
version: 2
updates:
- package-ecosystem: "gomod"
directory: "/"
schedule:
interval: "weekly"
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
+3 -3
View File
@@ -13,11 +13,11 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Set up Go
uses: actions/setup-go@v5
uses: actions/setup-go@v2
with:
go-version: '1.20.2'
go-version: ^1.15
- name: Check out code
uses: actions/checkout@v4
uses: actions/checkout@v2
- name: Build
run: make build
- name: Test
-3
View File
@@ -21,6 +21,3 @@ main
# Build
/bin
# Logs from executed scripts
/scripts/output.log
-236
View File
@@ -1,236 +0,0 @@
{
"version": "2.0.0",
"tasks": [
{
"label": "Build",
"type": "shell",
"command": "make",
"args": ["build"],
"options": {
"cwd": "${workspaceFolder}"
},
"group": {
"kind": "build",
"isDefault": true
},
"problemMatcher": []
},
{
"label": "Build All",
"type": "shell",
"command": "make",
"args": ["build-all"],
"options": {
"cwd": "${workspaceFolder}"
},
"group": "build",
"problemMatcher": []
},
{
"label": "Build Prod",
"type": "shell",
"command": "make",
"args": ["build-prod"],
"options": {
"cwd": "${workspaceFolder}"
},
"group": "build",
"problemMatcher": []
},
{
"label": "Build Cloud",
"type": "shell",
"command": "make",
"args": ["build-cloud"],
"options": {
"cwd": "${workspaceFolder}"
},
"group": "build",
"problemMatcher": []
},
{
"label": "Build ARM64",
"type": "shell",
"command": "make",
"args": ["build-arm64"],
"options": {
"cwd": "${workspaceFolder}"
},
"group": "build",
"problemMatcher": []
},
{
"label": "Build ARMHF",
"type": "shell",
"command": "make",
"args": ["build-armhf"],
"options": {
"cwd": "${workspaceFolder}"
},
"group": "build",
"problemMatcher": []
},
{
"label": "Build AMD64",
"type": "shell",
"command": "make",
"args": ["build-amd64"],
"options": {
"cwd": "${workspaceFolder}"
},
"group": "build",
"problemMatcher": []
},
{
"label": "Clean",
"type": "shell",
"command": "make",
"args": ["clean"],
"options": {
"cwd": "${workspaceFolder}"
},
"problemMatcher": []
},
{
"label": "Test",
"type": "shell",
"command": "make",
"args": ["test"],
"options": {
"cwd": "${workspaceFolder}"
},
"problemMatcher": []
},
{
"label": "Test with Coverage",
"type": "shell",
"command": "make",
"args": ["test-with-coverage"],
"options": {
"cwd": "${workspaceFolder}"
},
"problemMatcher": []
},
{
"label": "Run",
"type": "shell",
"command": "make",
"args": ["run"],
"options": {
"cwd": "${workspaceFolder}"
},
"problemMatcher": []
},
{
"label": "Install",
"type": "shell",
"command": "make",
"args": ["install"],
"options": {
"cwd": "${workspaceFolder}"
},
"problemMatcher": []
},
{
"label": "Install Prod",
"type": "shell",
"command": "make",
"args": ["install-prod"],
"options": {
"cwd": "${workspaceFolder}"
},
"problemMatcher": []
},
{
"label": "Install Cloud",
"type": "shell",
"command": "make",
"args": ["install-cloud"],
"options": {
"cwd": "${workspaceFolder}"
},
"problemMatcher": []
},
{
"label": "Install ARM64",
"type": "shell",
"command": "make",
"args": ["install-arm64"],
"options": {
"cwd": "${workspaceFolder}"
},
"problemMatcher": []
},
{
"label": "Install ARMHF",
"type": "shell",
"command": "make",
"args": ["install-armhf"],
"options": {
"cwd": "${workspaceFolder}"
},
"problemMatcher": []
},
{
"label": "Install AMD64",
"type": "shell",
"command": "make",
"args": ["install-amd64"],
"options": {
"cwd": "${workspaceFolder}"
},
"problemMatcher": []
},
{
"label": "Start",
"type": "shell",
"command": "make",
"args": ["start"],
"options": {
"cwd": "${workspaceFolder}"
},
"problemMatcher": []
},
{
"label": "Stop",
"type": "shell",
"command": "make",
"args": ["stop"],
"options": {
"cwd": "${workspaceFolder}"
},
"problemMatcher": []
},
{
"label": "Restart",
"type": "shell",
"command": "make",
"args": ["restart"],
"options": {
"cwd": "${workspaceFolder}"
},
"problemMatcher": []
},
{
"label": "Status",
"type": "shell",
"command": "make",
"args": ["status"],
"options": {
"cwd": "${workspaceFolder}"
},
"problemMatcher": []
},
{
"label": "Logs",
"type": "shell",
"command": "make",
"args": ["log"],
"options": {
"cwd": "${workspaceFolder}"
},
"problemMatcher": []
}
]
}
-29
View File
@@ -1,29 +0,0 @@
# Changelog
## [1.4.0] - 23-09-2026
* Added dashboard-managed SSH public-key access.
* Validates ED25519 public keys and reports their SHA256 fingerprints.
* Atomically installs or removes only the marked Edgebox key while preserving all other authorized keys.
* Never generates, reads, stores, logs, or returns SSH private keys.
## [1.3.2] - 08-12-2024
* Fix to Browser Dev feature:
* Checking browser dev url when internet_accessible was checking the incorrect path. This is now fixed.
## [1.3.1] - 08-12-2024
* Fixes to Browser Dev feature:
* Now edgeboxctl also fetches or generates the browser dev environment url and saves it into an option both when starting, and every time the browser dev status is fetched.
## [1.3.0] - 05-12-2024
* Added Edgebox Browser Development Environment Feature Support
* Added tasks for handling browser development environment into tasks.go
* Added executable tasks to ExecuteTask and scheduled ones to ExecuteSchedules
* Other bug fixes and improvements.
### Missing Past Releases
Release notes for past versions are not available in this file. Please refer to the [GitHub releases](https://hithub.com/edgebox-iot/edgeboxctl/releases) for more information. Feel free to contribute to this file by adding missing release notes.
+5 -3
View File
@@ -1,9 +1,11 @@
FROM golang:1.20.2
FROM golang:latest
WORKDIR /app
COPY ./ /app
RUN go install github.com/githubnemo/CompileDaemon@latest
RUN go mod download
ENTRYPOINT CompileDaemon --build="make build" --command=./bin/edgeboxctl-$(go env GOOS)-$(go env GOARCH)
RUN go get github.com/githubnemo/CompileDaemon
ENTRYPOINT CompileDaemon --build="make build" --command=./bin/edgeboxctl
+4 -83
View File
@@ -1,114 +1,35 @@
.DEFAULT_GOAL := build
PROJECT?=github.com/edgebox-iot/edgeboxctl
PROJECT ?= github.com/edgebox-iot/edgeboxctl
RELEASE ?= dev
COMMIT := $(shell git rev-parse --short HEAD)
BUILD_DATE := $(shell date -u '+%Y-%m-%d_%H:%M:%S')
BUILD_DIR = bin
GOOS := $(shell go env GOOS)
GOARCH := $(shell go env GOARCH)
build-all:
@echo "\n🏗️ Building all architectures for ${RELEASE} mode"
@echo "🟡 This will build all supported architectures and release combinations. It can take a while...\n"
GOOS=linux GOARCH=amd64 make build
GOOS=linux GOARCH=arm make build
@echo "\n🟢 All builds completed and available at ./bin/ \n"
build-prod:
GOOS=linux GOARCH=arm RELEASE=prod make build
build-cloud:
GOOS=linux GOARCH=amd64 RELEASE=cloud make build
build-arm64:
GOOS=linux GOARCH=arm64 RELEASE=prod make build
build-armhf:
GOOS=linux GOARCH=arm RELEASE=prod make build
build-amd64:
GOOS=linux GOARCH=amd64 RELEASE=prod make build
CGO_ENABLED=1 GOOS=linux GOARCH=amd64 RELEASE=cloud make build
build:
@echo "\n🏗️ Building edgeboxctl (${RELEASE} release) on ${GOOS} (${GOARCH})"
@echo "📦 Binary will be saved in ./${BUILD_DIR}/edgeboxctl-${GOOS}-${GOARCH}\n"
@echo "Building ${GOOS}-${GOARCH}"
GOOS=${GOOS} GOARCH=${GOARCH} go build \
-trimpath -ldflags "-s -w -X ${PROJECT}/internal/diagnostics.Version=${RELEASE} \
-X ${PROJECT}/internal/diagnostics.Commit=${COMMIT} \
-X ${PROJECT}/internal/diagnostics.BuildDate=${BUILD_DATE}" \
-o bin/edgeboxctl-${GOOS}-${GOARCH} ${PROJECT}/cmd/edgeboxctl
@echo "\n🟢 Build task completed\n"
cp ./bin/edgeboxctl-${GOOS}-${GOARCH} ./bin/edgeboxctl
clean:
@echo "🧹 Cleaning build directory and go cache\n"
rm -rf ${BUILD_DIR}
go clean
@echo "\n🟢 Clean task completed\n"
test:
go test -tags=unit -timeout=600s -v ./...
test-with-coverage:
go test -tags=unit -timeout=600s -v ./... -coverprofile=coverage.out
run:
@echo "\n🚀 Running edgeboxctl\n"
./bin/edgeboxctl-${GOOS}-${GOARCH}
install:
@echo "📦 Installing edgeboxctl service (${RELEASE}) for ${GOOS} (${GOARCH})\n"
@echo "🚧 Stopping edgeboxctl service if it is running"
sudo systemctl stop edgeboxctl || true
@echo "\n🗑️ Removing old edgeboxctl binary and service"
sudo rm -rf /usr/local/bin/edgeboxctl /usr/local/sbin/edgeboctl /lib/systemd/system/edgeboxctl.service
@echo "\n🚚 Copying edgeboxctl binary to /usr/local/bin"
sudo cp ./bin/edgeboxctl-${GOOS}-${GOARCH} /usr/local/bin/edgeboxctl
sudo cp ./bin/edgeboxctl-${GOOS}-${GOARCH} /usr/local/sbin/edgeboxctl
@echo "\n🚚 Copying edgeboxctl service to /lib/systemd/system"
sudo cp ./edgeboxctl.service /lib/systemd/system/edgeboxctl.service
sudo systemctl daemon-reload
@echo "\n 🚀 To start edgeboxctl run: make start"
@echo "🟢 Edgeboxctl installed successfully\n"
install-prod: build-prod install
install-cloud: build-cloud install
install-arm64: build-arm64 install
install-armhf: build-armhf install
install-amd64: build-amd64 install
start:
@echo "\n 🚀 Starting edgeboxctl service\n"
systemctl start edgeboxctl
@echo "\n 🟢 Edgebox service started\n"
stop:
@echo "\n✋ Stopping edgeboxctl service\n"
systemctl stop edgeboxctl
@echo "\n 🟢 Edgebox service stopped\n"
restart:
@echo "\n💫 Restarting edgeboxctl service\n"
systemctl restart edgeboxctl
@echo "\n 🟢 Edgebox service restarted\n"
status:
@echo "\n️ edgeboxctl Service Info:\n"
systemctl status edgeboxctl
log:
@echo "\n📰 edgeboxctl service logs:\n"
journalctl -fu edgeboxctl
+52 -13
View File
@@ -1,3 +1,21 @@
<!-- PROJECT SHIELDS -->
<!--
*** Using markdown "reference style" links for readability.
*** Reference links are enclosed in brackets [ ] instead of parentheses ( ).
*** See the bottom of this document for the declaration of the reference variables
*** for contributors-url, forks-url, etc. This is an optional, concise syntax you may use.
*** https://www.markdownguide.org/basic-syntax/#reference-style-links
-->
[![Contributors][contributors-shield]][contributors-url]
[![Forks][forks-shield]][forks-url]
[![Stargazers][stars-shield]][stars-url]
[![Issues][issues-shield]][issues-url]
[![MIT License][license-shield]][license-url]
[![LinkedIn][linkedin-shield]][linkedin-url]
<!-- PROJECT LOGO -->
<br />
<p align="center">
@@ -8,7 +26,9 @@
<h3 align="center">Edgebox Control Module</h3>
<p align="center">
A System Control module written in Go. Its responsibility is to configure dependencies and perform system tasks, automatically on a schedule, or on demand.
A System Control module written in Go. Its resonsability is to configure dependencies and perform system tasks, automatically in a schedule, or by command.
<br />
<a href="https://github.com/github_username/edgeboxctl"><strong>Explore the docs »</strong></a>
<br />
<br />
<a href="https://github.com/github_username/edgeboxctl/issues">Report Bug</a>
@@ -38,7 +58,9 @@
<!-- ABOUT THE PROJECT -->
## About The Project
Edgebox is an easy to configure and use system. It brings powerful features that go alongside or can even completely replace various services that you already use in the day-to-day.
[![Edgebox Screen Shot][product-screenshot]](https://edgebox.co)
Edgebox is a small box that connects alongside your home internet router. It brings powerful features that go alongside or can even completely replace various services that you already use in the day-to-day.
### Built With
@@ -56,17 +78,18 @@ To get a local copy up and running follow these simple steps.
### Prerequisites
If you're running for development purposes, a docker container suffices, so make sure you have:
If you're installing this to run natively in the system, you better be doing it inside of the [Development Virtual Machine](https://github.com/edgebox-iot/devm). This software can do destructive action in the system is is running. You've been warned.
* docker
* docker compose (docker-compose-v2 package)
If you're running for development purposes, a Docker container suffices, so make sure you have:
* Docker
* Docker Compose
```sh
sudo apt-get install docker docker-compose
```
Check the following links for more info on [Docker](https://www.docker.com/) and [Docker Compose](https://docs.docker.com/compose/).
**Note:** If you don't have `docker compose` available, install it with: `sudo apt-get install docker-compose-v2`
Aditionally, `edgeboxctl` needs the following bash commands available wherever it runs:
Aditionally, edgeboxctl needs the following bash commands available wherever it runs:
* `arm-linux-gnueabi-gcc` (`sudo apt-get install gcc-arm*`)
* `sh`
* `rm`
@@ -81,10 +104,10 @@ Aditionally, `edgeboxctl` needs the following bash commands available wherever i
```sh
git clone https://github.com/edgebox-iot/edgeboxctl.git
```
2. Run Docker Compose
2. Run Docker-Compose
```sh
docker-compose up
```
docker compose up
@@ -118,4 +141,20 @@ Contributions are what make the open source community such an amazing place to b
<!-- LICENSE -->
## License
Distributed under the Elastic License 2.0. See `LICENSE` for more information.
Distributed under the MIT License. See `LICENSE` for more information.
<!-- MARKDOWN LINKS & IMAGES -->
<!-- https://www.markdownguide.org/basic-syntax/#reference-style-links -->
[contributors-shield]: https://img.shields.io/github/contributors/edgebox-iot/edgeboxctl.svg?style=flat-square
[contributors-url]: https://github.com/edgebox-iot/edgeboxctl/graphs/contributors
[forks-shield]: https://img.shields.io/github/forks/edgebox-iot/edgeboxctl.svg?style=flat-square
[forks-url]: https://github.com/edgebox-iot/edgeboxctl/network/members
[stars-shield]: https://img.shields.io/github/stars/edgebox-iot/edgeboxctl.svg?style=flat-square
[stars-url]: https://github.com/edgebox-iot/edgeboxctl/stargazers
[issues-shield]: https://img.shields.io/github/issues/edgebox-iot/edgeboxctl.svg?style=flat-square
[issues-url]: https://github.com/edgebox-iot/edgeboxctl/issues
[license-shield]: https://img.shields.io/github/license/edgebox-iot/edgeboxctl.svg?style=flat-square
[license-url]: https://github.com/edgebox-iot/edgeboxctl/blob/master/LICENSE.txt
[linkedin-shield]: https://img.shields.io/badge/-LinkedIn-black.svg?style=flat-square&logo=linkedin&colorB=555
[linkedin-url]: https://linkedin.com/in/edgebox-iot
+288 -30
View File
@@ -1,7 +1,6 @@
package main
import (
"flag"
"fmt"
"log"
"os"
@@ -12,32 +11,304 @@ import (
"github.com/edgebox-iot/edgeboxctl/internal/diagnostics"
"github.com/edgebox-iot/edgeboxctl/internal/tasks"
"github.com/edgebox-iot/edgeboxctl/internal/utils"
"github.com/mitchellh/colorstring"
"github.com/urfave/cli/v2" // imports as package "cli",
)
const defaultNotReadySleepTime time.Duration = time.Second * 60
const defaultSleepTime time.Duration = time.Second
var errorMissingApplicationSlug = colorstring.Color("[red]Error: [white]Missing application slug")
var errorSystemNotReady = colorstring.Color("[red]Eror: [white]The system is not ready to run. \n[white]Please run 'cd /home/system/components/ws/ && ./ws -b' first.")
var errorNotYetImplemented = colorstring.Color("[yellow]This feature is not yet implemented, this command is only a stub.")
func main() {
// load command line arguments
app := &cli.App{
Name: "edgeboxctl",
Usage: "A tool to facilitate hosting apps and securing your personal data",
Action: func(c *cli.Context) error {
startService() // Defaults to start as a service if no commands or flags are passed
return nil
},
Commands: []*cli.Command{
{
Name: "bootstrap",
Aliases: []string{"b"},
Usage: "Sets up initial structure and dependencies for the edgebox system",
Action: func(c *cli.Context) error {
return cli.Exit(errorNotYetImplemented, 1)
},
},
{
Name: "tunnel",
Aliases: []string{"t"},
Usage: "Edgebox tunnel settings",
Subcommands: []*cli.Command{
{
Name: "setup",
Aliases: []string{"s"},
Usage: "Sets up an encrypted secure connection to a tunnel",
Before: canRunCommands,
Action: func(c *cli.Context) error {
version := flag.Bool("version", false, "Get the version info")
db := flag.Bool("database", false, "Get database connection info")
name := flag.String("name", "edgebox", "Name for the service")
argumentError := checkArgumentsPresence(c, 4)
if argumentError != nil {
return argumentError
}
flag.Parse()
task := getCommandTask(
"setup_tunnel",
fmt.Sprintf(
"{\"bootnode_address\": \"%s\", \"token\": \"%s\", \"assigned_address\": \"%s\", \"node_name\": \"%s\"}",
c.Args().Get(0),
c.Args().Get(1),
c.Args().Get(2),
c.Args().Get(3),
),
true,
)
if *version {
printVersion()
os.Exit(0)
return cli.Exit(utils.ColorJsonString(task.Result.String), 0)
},
},
{
Name: "enable",
Aliases: []string{"e"},
Usage: "enables the public tunnel (when configured)",
Before: canRunCommands,
Action: func(c *cli.Context) error {
return cli.Exit(errorNotYetImplemented, 1)
},
},
{
Name: "disable",
Aliases: []string{"d"},
Usage: "disables the public tunnel (when online)",
Before: canRunCommands,
Action: func(c *cli.Context) error {
return cli.Exit(errorNotYetImplemented, 1)
},
},
},
},
{
Name: "app",
Aliases: []string{"a"},
Usage: "options for edgeapp management",
Subcommands: []*cli.Command{
{
Name: "list",
Aliases: []string{"l"},
Usage: "list currently installed apps and their status",
Before: canRunCommands,
Action: func(c *cli.Context) error {
task := getCommandTask("list_edgeapps", "", true)
// return cli.Exit(utils.ColorJsonString(task.Result.String), 0)
return cli.Exit(task.Result.String, 0)
},
},
{
Name: "install",
Aliases: []string{"i"},
Usage: "install the specified app (slug or package file)",
Before: canRunCommands,
Action: func(c *cli.Context) error {
argumentError := checkArgumentsPresence(c, 1)
if argumentError != nil {
return argumentError
}
task := getCommandTask("install_edgeapp", fmt.Sprintf("{\"id\": \"%s\"}", c.Args().First()), true)
return cli.Exit(utils.ColorJsonString(task.Result.String), 0)
},
},
{
Name: "remove",
Aliases: []string{"r"},
Usage: "remove the specified app",
Before: canRunCommands,
Action: func(c *cli.Context) error {
argumentError := checkArgumentsPresence(c, 1)
if argumentError != nil {
return argumentError
}
task := getCommandTask("remove_edgeapp", fmt.Sprintf("{\"id\": \"%s\"}", c.Args().First()), true)
return cli.Exit(utils.ColorJsonString(task.Result.String), 0)
},
},
{
Name: "start",
Aliases: []string{"s"},
Usage: "start the specified app",
Before: canRunCommands,
Action: func(c *cli.Context) error {
argumentError := checkArgumentsPresence(c, 1)
if argumentError != nil {
return argumentError
}
task := getCommandTask("start_edgeapp", fmt.Sprintf("{\"id\": \"%s\"}", c.Args().First()), true)
return cli.Exit(utils.ColorJsonString(task.Result.String), 0)
},
},
{
Name: "stop",
Aliases: []string{"k"},
Usage: "stop the specified app",
Before: canRunCommands,
Action: func(c *cli.Context) error {
argumentError := checkArgumentsPresence(c, 1)
if argumentError != nil {
return argumentError
}
task := getCommandTask("stop_edgeapp", fmt.Sprintf("{\"id\": \"%s\"}", c.Args().First()), true)
return cli.Exit(utils.ColorJsonString(task.Result.String), 0)
},
},
{
Name: "online",
Aliases: []string{"o"},
Usage: "set an app status for online access",
Subcommands: []*cli.Command{
{
Name: "enable",
Aliases: []string{"e"},
Usage: "set an app as accessible online",
Before: canRunCommands,
Action: func(c *cli.Context) error {
argumentError := checkArgumentsPresence(c, 2)
if argumentError != nil {
return argumentError
}
task := getCommandTask(
"enable_online",
fmt.Sprintf(
"{\"id\": \"%s\", \"internet_url\": \"%s\"}",
c.Args().Get(0),
c.Args().Get(1),
),
true,
)
return cli.Exit(utils.ColorJsonString(task.Result.String), 0)
},
},
{
Name: "disable",
Aliases: []string{"d"},
Usage: "set an app as local-network private",
Before: canRunCommands,
Action: func(c *cli.Context) error {
argumentError := checkArgumentsPresence(c, 1)
if argumentError != nil {
return argumentError
}
task := getCommandTask("disable_online", fmt.Sprintf("{\"id\": \"%s\"}", c.Args().First()), true)
return cli.Exit(utils.ColorJsonString(task.Result.String), 0)
},
},
},
},
},
},
{
Name: "dashboard",
Aliases: []string{"d"},
Usage: "set dashboard access",
Subcommands: []*cli.Command{
{
Name: "enable",
Aliases: []string{"e"},
Usage: "enable dashboard access",
Before: canRunCommands,
Action: func(c *cli.Context) error {
argumentError := checkArgumentsPresence(c, 1)
if argumentError != nil {
return argumentError
}
task := getCommandTask("enable_public_dashboard", fmt.Sprintf("{\"internet_url\": \"%s\"}", c.Args().First()), true)
return cli.Exit(utils.ColorJsonString(task.Result.String), 0)
},
},
{
Name: "disable",
Aliases: []string{"d"},
Usage: "disable dashboard access",
Before: canRunCommands,
Action: func(c *cli.Context) error {
argumentError := checkArgumentsPresence(c, 1)
if argumentError != nil {
return argumentError
}
task := getCommandTask("disable_public_dashboard", "", true)
return cli.Exit(utils.ColorJsonString(task.Result.String), 0)
},
},
},
},
},
}
if *db {
printDbDetails()
os.Exit(0)
err := app.Run(os.Args)
if err != nil {
log.Fatal(err)
}
}
func canRunCommands(c *cli.Context) error {
if !utils.IsSystemReady() {
return cli.Exit(errorSystemNotReady, 1)
}
log.Printf("Starting edgeboxctl service for %s", *name)
return nil
}
func checkArgumentsPresence(c *cli.Context, count int) error {
if count != 0 {
for i := 0; i < count; i++ {
if c.Args().Get(i) == "" {
return cli.Exit(errorMissingApplicationSlug, 1)
}
}
}
return nil
}
func getCommandTask(taskSlug string, taskArgs string, execute bool) tasks.Task {
task := tasks.GetBaseTask(
taskSlug,
taskArgs,
)
if execute {
task = tasks.ExecuteTask(task)
}
return task
}
func startService() {
log.Printf("Starting edgeboxctl service")
// setup signal catching
sigs := make(chan os.Signal, 1)
@@ -57,7 +328,6 @@ func main() {
}()
printVersion()
printDbDetails()
tick := 0
@@ -65,9 +335,9 @@ func main() {
// infinite loop
for {
if isSystemReady() {
if utils.IsSystemReady() {
tick++ // Tick is an int, so eventually will "go out of ticks?" Maybe we want to reset the ticks every once in a while, to avoid working with big numbers...
systemIterator(name, tick)
systemIterator(tick)
} else {
// Wait about 60 seconds before trying again.
log.Printf("System not ready. Next try will be executed in 60 seconds")
@@ -75,7 +345,6 @@ func main() {
}
}
}
// AppCleanup : cleanup app state before exit
@@ -97,18 +366,7 @@ func printDbDetails() {
)
}
// IsSystemReady : Checks hability of the service to execute commands (Only after "edgebox --build" is ran at least once via SSH, or if built for distribution)
func isSystemReady() bool {
_, err := os.Stat(utils.GetPath(utils.WsPath) + ".ready")
return !os.IsNotExist(err)
}
// IsDatabaseReady : Checks is it can successfully connect to the task queue db
func isDatabaseReady() bool {
return false
}
func systemIterator(name *string, tick int) {
func systemIterator(tick int) {
log.Printf("Tick is %d", tick)
@@ -125,7 +383,7 @@ func systemIterator(name *string, tick int) {
log.Printf("No tasks to execute.")
}
// Wait about 1 second before resumming operations.
// Wait about 1 second before resuming operations.
log.Printf("Next instruction will be executed 1 second")
time.Sleep(defaultSleepTime)
-29
View File
@@ -1,29 +0,0 @@
[Unit]
Description=Edgebox Control Module Service
ConditionPathExists=/home/system/
After=network.target
[Service]
Type=simple
User=root
Group=root
LimitNOFILE=1024
Restart=on-failure
RestartSec=10
startLimitIntervalSec=60
WorkingDirectory=/home/system/components/edgeboxctl
ExecStart=edgeboxctl --name=edgebox-cloud
# make sure log directory exists and owned by syslog
PermissionsStartOnly=true
ExecStartPre=/bin/mkdir -p /var/log/edgeboxctl
ExecStartPre=/bin/chown root:root /var/log/edgeboxctl
ExecStartPre=/bin/chmod 755 /var/log/edgeboxctl
StandardOutput=syslog
StandardError=syslog
SyslogIdentifier=edgeboxctl
[Install]
WantedBy=multi-user.target
+6 -2
View File
@@ -4,15 +4,19 @@ go 1.15
require (
github.com/StackExchange/wmi v0.0.0-20210224194228-fe8f1750fd46 // indirect
github.com/TylerBrock/colorjson v0.0.0-20200706003622-8a50f05110d2 // indirect
github.com/dariubs/percent v0.0.0-20200128140941-b7801cf1c7e2 // indirect
github.com/dustin/go-humanize v1.0.0 // indirect
github.com/fatih/color v1.13.0 // indirect
github.com/go-ole/go-ole v1.2.5 // indirect
github.com/go-sql-driver/mysql v1.5.0
github.com/go-yaml/yaml v2.1.0+incompatible // indirect
github.com/joho/godotenv v1.3.0
github.com/mattn/go-colorable v0.1.12 // indirect
github.com/mattn/go-sqlite3 v1.14.7 // indirect
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect
github.com/shirou/gopsutil v3.21.4+incompatible // indirect
github.com/tklauser/go-sysconf v0.3.6 // indirect
golang.org/x/sys v0.0.0-20210531080801-fdfd190a6549 // indirect
github.com/urfave/cli/v2 v2.3.0 // indirect
golang.org/x/sys v0.0.0-20220204135822-1c1b9b1eba6a // indirect
gopkg.in/yaml.v2 v2.4.0
)
+27 -2
View File
@@ -1,18 +1,22 @@
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
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/TylerBrock/colorjson v0.0.0-20200706003622-8a50f05110d2 h1:ZBbLwSJqkHBuFDA6DUhhse0IGJ7T5bemHyNILUjvOq4=
github.com/TylerBrock/colorjson v0.0.0-20200706003622-8a50f05110d2/go.mod h1:VSw57q4QFiWDbRnjdX8Cb3Ow0SFncRw+bA/ofY6Q83w=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/dariubs/percent v0.0.0-20200128140941-b7801cf1c7e2 h1:5EPE4Uk7ucthLTJAZqZxu6LZluox5/AqXUxJDpzgJjg=
github.com/dariubs/percent v0.0.0-20200128140941-b7801cf1c7e2/go.mod h1:NDZpkezJ8QqyIW/510MywB5T2KdC8v/0oTlEoPcMsRM=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
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-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/go-yaml/yaml v2.1.0+incompatible h1:RYi2hDdss1u4YE7GwixGzWwVo47T8UQwnTLB6vQiq+o=
github.com/go-yaml/yaml v2.1.0+incompatible/go.mod h1:w2MrLa16VYP0jy6N7M5kHaCkaLENm+P+Tv+MfurjSw0=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
@@ -20,13 +24,25 @@ github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+o
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40=
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/mattn/go-sqlite3 v1.14.7 h1:fxWBnXkxfM6sRiuH3bqJ4CfzZojMOLVc0UTsTglEghA=
github.com/mattn/go-sqlite3 v1.14.7/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2EmQ4l5rM/4FEfDWcRD+abF5XlKShorW5LRoQ=
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/shirou/gopsutil v3.21.4+incompatible h1:fuHcTm5mX+wzo542cmYcV9RTGQLbnHLI5SyQ5ryTVck=
github.com/shirou/gopsutil v3.21.4+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
@@ -34,6 +50,8 @@ github.com/tklauser/go-sysconf v0.3.6 h1:oc1sJWvKkmvIxhDHeKWvZS4f6AW+YcoguSfRF2/
github.com/tklauser/go-sysconf v0.3.6/go.mod h1:MkWzOF4RMCshBAMXuhXJs64Rte09mITnppBXY/rYEFI=
github.com/tklauser/numcpus v0.2.2 h1:oyhllyrScuYI6g+h/zUvNXNp1wy7x8qQy3t/piefldA=
github.com/tklauser/numcpus v0.2.2/go.mod h1:x3qojaO3uyYt0i56EW/VUYs7uBvdl2fkfZFu0T9wgjM=
github.com/urfave/cli/v2 v2.3.0 h1:qph92Y649prgesehzOrQjdWyxFOp/QVM+6imKHad91M=
github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
go.uber.org/atomic v1.6.0 h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk=
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
@@ -56,9 +74,15 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210316164454-77fc1eacc6aa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210531080801-fdfd190a6549 h1:OL5GcZ2XPkte3dpfuFQ9o884vrE3BZQhajdntNMruv4=
golang.org/x/sys v0.0.0-20210531080801-fdfd190a6549/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220204135822-1c1b9b1eba6a h1:ppl5mZgokTT8uPkmYOyEUmPTr3ypaKkg5eFOGrAmxxE=
golang.org/x/sys v0.0.0-20220204135822-1c1b9b1eba6a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
@@ -69,6 +93,7 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
-28
View File
@@ -1,28 +0,0 @@
package backups
// import (
// "fmt"
// "os"
// "path/filepath"
// "strings"
// "github.com/edgebox-iot/edgeboxctl/internal/diagnostics"
// "github.com/edgebox-iot/edgeboxctl/internal/utils"
// "github.com/shirou/gopsutil/disk"
// )
// Repository : Struct representing the backup repository of a device in the system
type Repository struct {
ID string `json:"id"`
FileCount int64 `json:"file_count"`
Size string `json:"size"`
Snapshots []Snapshot `json:"snapshots"`
Status string `json:"status"`
// UsageStat UsageStat `json:"usage_stat"`
}
// Snapshot : Struct representing a single snapshot in the backup repository
type Snapshot struct {
ID string `json:"id"`
time string `json:"time"`
}
+50 -319
View File
@@ -6,12 +6,10 @@ import (
"os"
"strings"
"time"
"github.com/joho/godotenv"
"github.com/edgebox-iot/edgeboxctl/internal/system"
"github.com/edgebox-iot/edgeboxctl/internal/utils"
"github.com/edgebox-iot/edgeboxctl/internal/diagnostics"
)
// EdgeApp : Struct representing an EdgeApp in the system
@@ -19,15 +17,11 @@ type EdgeApp struct {
ID string `json:"id"`
Name string `json:"name"`
Description string `json:"description"`
Experimental bool `json:"experimental"`
Status EdgeAppStatus `json:"status"`
Services []EdgeAppService `json:"services"`
InternetAccessible bool `json:"internet_accessible"`
NetworkURL string `json:"network_url"`
InternetURL string `json:"internet_url"`
Options []EdgeAppOption `json:"options"`
NeedsConfig bool `json:"needs_config"`
Login EdgeAppLogin `json:"login"`
}
// MaybeEdgeApp : Boolean flag for validation of edgeapp existance
@@ -48,30 +42,9 @@ type EdgeAppService struct {
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"`
}
type EdgeAppLogin struct {
Enabled bool `json:"enabled"`
Username string `json:"username"`
Password string `json:"password"`
}
const configFilename = "/edgebox-compose.yml"
const envFilename = "/edgebox.env"
const optionsTemplateFilename = "/edgeapp.template.env"
const optionsEnvFilename = "/edgeapp.env"
const authEnvFilename = "/auth.env"
const runnableFilename = "/.run"
const appdataFoldername = "/appdata"
const postInstallFilename = "/edgebox-postinstall.done"
const myEdgeAppServiceEnvFilename = "/myedgeapp.env"
const defaultContainerOperationSleepTime time.Duration = time.Second * 10
@@ -83,16 +56,14 @@ func GetEdgeApp(ID string) MaybeEdgeApp {
Valid: false,
}
_, err := os.Stat(utils.GetPath(utils.EdgeAppsPath) + ID + configFilename)
_, err := os.Stat(utils.GetPath("edgeAppsPath") + ID + configFilename)
if !os.IsNotExist(err) {
// File exists. Start digging!
edgeAppName := ID
edgeAppDescription := ""
edgeAppExperimental := false
edgeAppOptions := []EdgeAppOption{}
edgeAppEnv, err := godotenv.Read(utils.GetPath(utils.EdgeAppsPath) + ID + envFilename)
edgeAppEnv, err := godotenv.Read(utils.GetPath("edgeAppsPath") + ID + envFilename)
if err != nil {
log.Println("Error loading .env file for edgeapp " + edgeAppName)
@@ -103,114 +74,15 @@ func GetEdgeApp(ID string) MaybeEdgeApp {
if edgeAppEnv["EDGEAPP_DESCRIPTION"] != "" {
edgeAppDescription = edgeAppEnv["EDGEAPP_DESCRIPTION"]
}
if edgeAppEnv["EDGEAPP_EXPERIMENTAL"] == "true" {
edgeAppExperimental = true
}
}
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
edgeAppInternetURL := ""
myEdgeAppServiceEnv, err := godotenv.Read(utils.GetPath(utils.EdgeAppsPath) + ID + myEdgeAppServiceEnvFilename)
myEdgeAppServiceEnv, err := godotenv.Read(utils.GetPath("edgeAppsPath") + ID + myEdgeAppServiceEnvFilename)
if err != nil {
log.Println("No myedge.app environment file found. Status is Network-Only")
// log.Println("No myedge.app environment file found. Status is Network-Only")
// File probably not found!
} else {
if myEdgeAppServiceEnv["INTERNET_URL"] != "" {
edgeAppInternetAccessible = true
@@ -218,36 +90,16 @@ func GetEdgeApp(ID string) MaybeEdgeApp {
}
}
edgeAppBasicAuthEnabled := false
edgeAppBasicAuthUsername := ""
edgeAppBasicAuthPassword := ""
edgeAppAuthEnv, err := godotenv.Read(utils.GetPath(utils.EdgeAppsPath) + ID + authEnvFilename)
if err != nil {
log.Println("No auth.env file found. Login status is disabled.")
} else {
if edgeAppAuthEnv["USERNAME"] != "" && edgeAppAuthEnv["PASSWORD"] != "" {
edgeAppBasicAuthEnabled = true
edgeAppBasicAuthUsername = edgeAppAuthEnv["USERNAME"]
edgeAppBasicAuthPassword = edgeAppAuthEnv["PASSWORD"]
}
}
result = MaybeEdgeApp{
EdgeApp: EdgeApp{
ID: ID,
Name: edgeAppName,
Description: edgeAppDescription,
Experimental: edgeAppExperimental,
Status: GetEdgeAppStatus(ID),
Services: GetEdgeAppServices(ID),
InternetAccessible: edgeAppInternetAccessible,
NetworkURL: ID + "." + system.GetHostname() + ".local",
NetworkURL: ID + ".edgebox.local",
InternetURL: edgeAppInternetURL,
Options: edgeAppOptions,
NeedsConfig: needsConfig,
Login: EdgeAppLogin{edgeAppBasicAuthEnabled, edgeAppBasicAuthUsername, edgeAppBasicAuthPassword},
},
Valid: true,
}
@@ -262,7 +114,7 @@ func IsEdgeAppInstalled(ID string) bool {
result := false
_, err := os.Stat(utils.GetPath(utils.EdgeAppsPath) + ID + runnableFilename)
_, err := os.Stat(utils.GetPath("edgeAppsPath") + ID + runnableFilename)
if !os.IsNotExist(err) {
result = true
}
@@ -271,54 +123,21 @@ func IsEdgeAppInstalled(ID string) bool {
}
func writeAppRunnableFiles(ID string) bool {
edgeAppPath := utils.GetPath(utils.EdgeAppsPath)
_, err := os.Stat(edgeAppPath + ID + runnableFilename)
if os.IsNotExist(err) {
_, err := os.Create(edgeAppPath + ID + runnableFilename)
if err != nil {
log.Fatal("Runnable file for EdgeApp could not be created!")
return false
}
// Check the block default apps option
blockDefaultAppsOption := utils.ReadOption("DASHBOARD_BLOCK_DEFAULT_APPS_PUBLIC_ACCESS")
if blockDefaultAppsOption != "yes" {
// Create myedgeapp.env file with default network URL
envFilePath := edgeAppPath + ID + myEdgeAppServiceEnvFilename
var networkURL string
domainName := utils.ReadOption("DOMAIN_NAME")
if domainName != "" {
networkURL = ID + "." + domainName
} else if diagnostics.GetReleaseVersion() == diagnostics.CLOUD_VERSION {
cluster := utils.ReadOption("CLUSTER")
username := utils.ReadOption("USERNAME")
if cluster != "" && username != "" {
networkURL = username + "-" + ID + "." + cluster
}
} else {
networkURL = ID + "." + system.GetHostname() + ".local" // default
}
env, _ := godotenv.Unmarshal("INTERNET_URL=" + networkURL)
err = godotenv.Write(env, envFilePath)
if err != nil {
log.Printf("Error creating myedgeapp.env file: %s", err)
// result = false
}
}
}
return true
}
func SetEdgeAppInstalled(ID string) bool {
result := true
if writeAppRunnableFiles(ID) {
_, err := os.Stat(utils.GetPath("edgeAppsPath") + ID + runnableFilename)
if os.IsNotExist(err) {
_, err := os.Create(utils.GetPath("edgeAppsPath") + ID + runnableFilename)
result = true
if err != nil {
log.Fatal("Runnable file for EdgeApp could not be created!")
result = false
}
buildFrameworkContainers()
} else {
@@ -332,63 +151,13 @@ func SetEdgeAppInstalled(ID string) bool {
}
func SetEdgeAppBulkInstalled(IDs []string) bool {
result := true
for _, ID := range IDs {
writeAppRunnableFiles(ID)
}
buildFrameworkContainers()
return result
}
func SetEdgeAppNotInstalled(ID string) bool {
// Stop the app first
StopEdgeApp(ID)
// Now remove any files
result := true
err := os.Remove(utils.GetPath(utils.EdgeAppsPath) + ID + runnableFilename)
err := os.Remove(utils.GetPath("edgeAppsPath") + ID + runnableFilename)
if err != nil {
result = false
log.Println(err)
}
err = os.Remove(utils.GetPath(utils.EdgeAppsPath) + ID + authEnvFilename)
if err != nil {
result = false
log.Println(err)
}
err = os.RemoveAll(utils.GetPath(utils.EdgeAppsPath) + ID + appdataFoldername)
if err != nil {
result = false
log.Println(err)
}
err = os.Remove(utils.GetPath(utils.EdgeAppsPath) + ID + myEdgeAppServiceEnvFilename)
if err != nil {
result = false
log.Println(err)
}
err = os.Remove(utils.GetPath(utils.EdgeAppsPath) + ID + optionsEnvFilename)
if err != nil {
result = false
log.Println(err)
}
err = os.Remove(utils.GetPath(utils.EdgeAppsPath) + ID + postInstallFilename)
if err != nil {
result = false
log.Println(err)
log.Fatal(err)
}
buildFrameworkContainers()
@@ -404,7 +173,7 @@ func GetEdgeApps() []EdgeApp {
// Building list of available edgeapps in the system with their status
files, err := ioutil.ReadDir(utils.GetPath(utils.EdgeAppsPath))
files, err := ioutil.ReadDir(utils.GetPath("edgeAppsPath"))
if err != nil {
log.Fatal(err)
}
@@ -467,34 +236,22 @@ func GetEdgeAppStatus(ID string) EdgeAppStatus {
}
// GetEdgeAppServices : Returns a
// GetEdgeAppServices : Returns a list of services for a single edgeapp
func GetEdgeAppServices(ID string) []EdgeAppService {
wsPath := utils.GetPath(utils.WsPath)
cmdArgs := []string{"-r", ".services | keys[]", utils.GetPath(utils.EdgeAppsPath) + ID + configFilename}
servicesString := utils.Exec(utils.GetPath(utils.WsPath), "yq", cmdArgs)
cmdArgs := []string{"-r", ".services | keys[]", utils.GetPath("edgeAppsPath") + ID + configFilename}
servicesString := utils.Exec(utils.GetPath("wsPath"), "yq", cmdArgs)
serviceSlices := strings.Split(servicesString, "\n")
serviceSlices = utils.DeleteEmptySlices(serviceSlices)
var edgeAppServices []EdgeAppService
for _, serviceID := range serviceSlices {
shouldBeRunning := false
cmdArgs = []string{"-f", utils.GetPath("wsPath") + "/docker-compose.yml", "exec", "-T", serviceID, "echo", "'Service Check'"}
cmdResult := utils.Exec(utils.GetPath("wsPath"), "docker-compose", cmdArgs)
isRunning := false
// Is service "runnable" when .run lockfile in the app folder
_, err := os.Stat(utils.GetPath(utils.EdgeAppsPath) + ID + runnableFilename)
if !os.IsNotExist(err) {
shouldBeRunning = true
if cmdResult != "" {
isRunning = true
}
// Check if the service is actually running
if shouldBeRunning {
cmdArgs = []string{"-f", wsPath + "/docker-compose.yml", "exec", "-T", serviceID, "echo", "'Service Check'"}
cmdResult := utils.Exec(wsPath, "docker", append([]string{"compose"}, cmdArgs...))
if cmdResult != "" {
isRunning = true
}
}
edgeAppServices = append(edgeAppServices, EdgeAppService{ID: serviceID, IsRunning: isRunning})
}
@@ -504,14 +261,16 @@ func GetEdgeAppServices(ID string) []EdgeAppService {
// RunEdgeApp : Run an EdgeApp and return its most current status
func RunEdgeApp(ID string) EdgeAppStatus {
wsPath := utils.GetPath(utils.WsPath)
services := GetEdgeAppServices(ID)
cmdArgs := []string{}
for _, service := range services {
cmdArgs = []string{"-f", wsPath + "/docker-compose.yml", "start", service.ID}
utils.Exec(wsPath, "docker", append([]string{"compose"}, cmdArgs...))
cmdArgs = []string{"-f", utils.GetPath("wsPath") + "/docker-compose.yml", "start", service.ID}
utils.Exec(utils.GetPath("wsPath"), "docker-compose", cmdArgs)
}
// Wait for it to settle up before continuing...
@@ -523,13 +282,16 @@ func RunEdgeApp(ID string) EdgeAppStatus {
// StopEdgeApp : Stops an EdgeApp and return its most current status
func StopEdgeApp(ID string) EdgeAppStatus {
wsPath := utils.GetPath(utils.WsPath)
services := GetEdgeAppServices(ID)
cmdArgs := []string{}
for _, service := range services {
cmdArgs = []string{"-f", wsPath + "/docker-compose.yml", "stop", service.ID}
utils.Exec(wsPath, "docker", append([]string{"compose"}, cmdArgs...))
cmdArgs = []string{"-f", utils.GetPath("wsPath") + "/docker-compose.yml", "stop", service.ID}
utils.Exec(utils.GetPath("wsPath"), "docker-compose", cmdArgs)
}
// Wait for it to settle up before continuing...
@@ -539,43 +301,13 @@ func StopEdgeApp(ID string) EdgeAppStatus {
}
// StopAllEdgeApps: Stops all EdgeApps and returns a count of how many were stopped
func StopAllEdgeApps() int {
edgeApps := GetEdgeApps()
appCount := 0
for _, edgeApp := range edgeApps {
StopEdgeApp(edgeApp.ID)
appCount++
}
return appCount
}
// StartAllEdgeApps: Starts all EdgeApps and returns a count of how many were started
func StartAllEdgeApps() int {
edgeApps := GetEdgeApps()
appCount := 0
for _, edgeApp := range edgeApps {
RunEdgeApp(edgeApp.ID)
appCount++
}
return appCount
}
func RestartEdgeAppsService() {
buildFrameworkContainers()
}
// EnableOnline : Write environment file and rebuild the necessary containers. Rebuilds containers in project (in case of change only)
func EnableOnline(ID string, InternetURL string) MaybeEdgeApp {
maybeEdgeApp := GetEdgeApp(ID)
if maybeEdgeApp.Valid { // We're only going to do this operation if the EdgeApp actually exists.
// Create the myedgeapp.env file and add the InternetURL entry to it
envFilePath := utils.GetPath(utils.EdgeAppsPath) + ID + myEdgeAppServiceEnvFilename
envFilePath := utils.GetPath("edgeAppsPath") + ID + myEdgeAppServiceEnvFilename
env, _ := godotenv.Unmarshal("INTERNET_URL=" + InternetURL)
_ = godotenv.Write(env, envFilePath)
}
@@ -589,13 +321,13 @@ func EnableOnline(ID string, InternetURL string) MaybeEdgeApp {
// DisableOnline : Removes env files necessary for system external access config. Rebuilds containers in project (in case of change only).
func DisableOnline(ID string) MaybeEdgeApp {
envFilePath := utils.GetPath(utils.EdgeAppsPath) + ID + myEdgeAppServiceEnvFilename
envFilePath := utils.GetPath("edgeAppsPath") + ID + myEdgeAppServiceEnvFilename
_, err := godotenv.Read(envFilePath)
if err != nil {
log.Println("myedge.app environment file for " + ID + " not found. No need to delete.")
} else {
cmdArgs := []string{envFilePath}
utils.Exec(utils.GetPath(utils.WsPath), "rm", cmdArgs)
utils.Exec(utils.GetPath("wsPath"), "rm", cmdArgs)
}
buildFrameworkContainers()
@@ -606,7 +338,7 @@ func DisableOnline(ID string) MaybeEdgeApp {
func EnablePublicDashboard(InternetURL string) bool {
envFilePath := utils.GetPath(utils.ApiPath) + myEdgeAppServiceEnvFilename
envFilePath := utils.GetPath("apiPath") + myEdgeAppServiceEnvFilename
env, _ := godotenv.Unmarshal("INTERNET_URL=" + InternetURL)
_ = godotenv.Write(env, envFilePath)
@@ -617,29 +349,28 @@ func EnablePublicDashboard(InternetURL string) bool {
}
func DisablePublicDashboard() bool {
envFilePath := utils.GetPath(utils.ApiPath) + myEdgeAppServiceEnvFilename
envFilePath := utils.GetPath("apiPath") + myEdgeAppServiceEnvFilename
if !IsPublicDashboard() {
log.Println("myedge.app environment file for the dashboard / api not found. No need to delete.")
return false
}
cmdArgs := []string{envFilePath}
utils.Exec(utils.GetPath(utils.ApiPath), "rm", cmdArgs)
utils.Exec(utils.GetPath("apiPath"), "rm", cmdArgs)
buildFrameworkContainers()
return true
}
func IsPublicDashboard() bool {
envFilePath := utils.GetPath(utils.ApiPath) + myEdgeAppServiceEnvFilename
envFilePath := utils.GetPath("apiPath") + myEdgeAppServiceEnvFilename
_, err := godotenv.Read(envFilePath)
return err == nil
}
func buildFrameworkContainers() {
wsPath := utils.GetPath(utils.WsPath)
cmdArgs := []string{wsPath + "ws", "--build"}
utils.ExecAndStream(wsPath, "sh", cmdArgs)
cmdArgs := []string{utils.GetPath("wsPath") + "ws", "--build"}
utils.ExecAndStream(utils.GetPath("wsPath"), "sh", cmdArgs)
time.Sleep(defaultContainerOperationSleepTime)
+5 -27
View File
@@ -2,7 +2,6 @@ package storage
import (
"fmt"
"strconv"
"os"
"path/filepath"
"strings"
@@ -74,13 +73,12 @@ const (
DISK_TYPE_SDA DeviceIdentifier = "sda"
DISK_TYPE_MCBLK DeviceIdentifier = "mmcblk0"
DISK_TYPE_VDA DeviceIdentifier = "vda"
MIN_DISK_SIZE int = 1048576 // 1GB in bytes
)
func GetDeviceIdentifier(release_version diagnostics.ReleaseVersion) DeviceIdentifier {
switch release_version {
case diagnostics.CLOUD_VERSION:
return DISK_TYPE_SDA
return DISK_TYPE_VDA
case diagnostics.PROD_VERSION:
return DISK_TYPE_MCBLK
}
@@ -136,13 +134,7 @@ func GetDevices(release_version diagnostics.ReleaseVersion) []Device {
currentDevice.InUse = currentDeviceInUseFlag
currentDeviceInUseFlag = false
currentPartitions = []Partition{}
size, err := strconv.Atoi(currentDevice.Size)
if err != nil {
size = 0
}
if size > MIN_DISK_SIZE {
devices = append(devices, currentDevice)
}
devices = append(devices, currentDevice)
} else {
firstDevice = false
}
@@ -201,22 +193,8 @@ func GetDevices(release_version diagnostics.ReleaseVersion) []Device {
currentDevice.Status.Description = "Not configured"
}
currentDevice.InUse = currentDeviceInUseFlag
devices = append([]Device{currentDevice}, devices...) // Prepending the first device...
fmt.Println("Secondary Storage Devices Found: ", len(devices))
fmt.Println("Main Storage Device size: ", currentDevice.Size)
// only append device if size > 1GB
if currentDevice.Size != "" && currentDevice.Size != "0" {
// Convert size to int
// Convert string to int
size, err := strconv.Atoi(currentDevice.Size)
if err != nil {
size = 0
}
if size > MIN_DISK_SIZE {
devices = append([]Device{currentDevice}, devices...) // Prepending the first device...
}
}
devices = getDevicesSpaceUsage(devices)
return devices
@@ -246,9 +224,9 @@ func getDevicesSpaceUsage(devices []Device) []Device {
bucketsUsageSplit := (uint64)(0)
othersUsageSplit := (uint64)(0)
edgeappsDirSize, _ := getDirSize(utils.GetPath(utils.EdgeAppsPath))
edgeappsDirSize, _ := getDirSize(utils.GetPath("edgeAppsPath"))
// TODO for later: Figure out to get correct paths for each partition...
wsAppDataDirSize, _ := getDirSize(utils.GetPath(utils.WsPath) + "/appdata")
wsAppDataDirSize, _ := getDirSize("/home/system/components/ws/appdata")
if partition.Mountpoint == "/" {
edgeappsUsageSplit = edgeappsDirSize + wsAppDataDirSize
+6 -518
View File
@@ -4,28 +4,13 @@ import (
"fmt"
"strconv"
"strings"
"log"
"os"
"io"
"errors"
"os/exec"
"bufio"
"path/filepath"
"io/ioutil"
"encoding/json"
"github.com/edgebox-iot/edgeboxctl/internal/utils"
"github.com/joho/godotenv"
"github.com/shirou/gopsutil/host"
"github.com/go-yaml/yaml"
)
"github.com/joho/godotenv"
type cloudflaredTunnelJson struct {
AccountTag string `json:"AccountTag"`
TunnelSecret string `json:"TunnelSecret"`
TunnelID string `json:"TunnelID"`
}
)
// GetUptimeInSeconds: Returns a value (as string) of the total system uptime
func GetUptimeInSeconds() string {
@@ -44,7 +29,7 @@ func GetUptimeFormatted() string {
return fmt.Sprintf("%d days, %d hours, %d minutes", days, hours, minutes)
}
// GetIP: Returns the ip address of the instance
// GetIP: Returns the ip address of the instance
func GetIP() string {
ip := ""
@@ -79,8 +64,7 @@ func GetHostname() string {
func SetupCloudOptions() {
var cloudEnv map[string]string
cloudEnvFileLocationPath := utils.GetPath(utils.CloudEnvFileLocation)
cloudEnv, err := godotenv.Read(cloudEnvFileLocationPath)
cloudEnv, err := godotenv.Read(utils.GetPath("cloudEnvFileLocation"))
if err != nil {
fmt.Println("Error loading .env file for cloud version setup")
@@ -94,507 +78,11 @@ func SetupCloudOptions() {
utils.WriteOption("EMAIL", cloudEnv["EMAIL"])
}
if cloudEnv["USERNAME"] != "" {
utils.WriteOption("USERNAME", cloudEnv["USERNAME"])
}
if cloudEnv["CLUSTER"] != "" {
utils.WriteOption("CLUSTER", cloudEnv["CLUSTER"])
}
if cloudEnv["CLUSTER_IP"] != "" {
utils.WriteOption("CLUSTER_IP", cloudEnv["CLUSTER_IP"])
}
if cloudEnv["CLUSTER_SSH_PORT"] != "" {
utils.WriteOption("CLUSTER_SSH_PORT", cloudEnv["CLUSTER_SSH_PORT"])
}
if cloudEnv["EDGEBOXIO_API_TOKEN"] != "" {
utils.WriteOption("EDGEBOXIO_API_TOKEN", cloudEnv["EDGEBOXIO_API_TOKEN"])
}
// In the end of this operation takes place, remove the env file as to not overwrite any options once they are set.
utils.Exec("/", "rm", []string{cloudEnvFileLocationPath})
utils.Exec("/", "rm", []string{utils.GetPath("cloudEnvFileLocation")})
}
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
func StartWs() {
wsPath := utils.GetPath(utils.WsPath)
fmt.Println("Starting WS")
cmdargs := []string{"-b"}
utils.Exec(wsPath, "./ws", cmdargs)
}
// StartService: Starts a service
func StartService(serviceID string) {
wsPath := utils.GetPath(utils.WsPath)
fmt.Println("Starting" + serviceID + "service")
cmdargs := []string{"start", serviceID}
utils.Exec(wsPath, "systemctl", cmdargs)
}
// StopService: Stops a service
func StopService(serviceID string) {
wsPath := utils.GetPath(utils.WsPath)
fmt.Println("Stopping" + serviceID + "service")
cmdargs := []string{"stop", "cloudflared"}
utils.Exec(wsPath, "systemctl", cmdargs)
}
// RestartService: Restarts a service
func RestartService(serviceID string) {
wsPath := utils.GetPath(utils.WsPath)
fmt.Println("Restarting" + serviceID + "service")
cmdargs := []string{"restart", serviceID}
utils.Exec(wsPath, "systemctl", cmdargs)
}
// GetServiceStatus: Returns the status output of a service
func GetServiceStatus(serviceID string) string {
wsPath := utils.GetPath(utils.WsPath)
cmdargs := []string{"status", serviceID}
return utils.Exec(wsPath, "systemctl", cmdargs)
}
func CreateBackupsPasswordFile(password string) {
// Create a password file for backups
backupPasswordFile := utils.GetPath(utils.BackupPasswordFileLocation)
backupPasswordFileDir := filepath.Dir(backupPasswordFile)
if _, err := os.Stat(backupPasswordFileDir); os.IsNotExist(err) {
os.MkdirAll(backupPasswordFileDir, 0755)
}
// Write the password to the file, overriting an existing file
err := ioutil.WriteFile(backupPasswordFile, []byte(password), 0644)
if err != nil {
panic(err)
}
}
// CreateTunnel: Creates a tunnel via cloudflared, needs to be authenticated first
func CreateTunnel(configDestination string) {
fmt.Println("Creating Tunnel for Edgebox.")
cmd := exec.Command("sh", "/home/system/components/edgeboxctl/scripts/cloudflared_tunnel_create.sh")
stdout, err := cmd.StdoutPipe()
if err != nil {
panic(err)
}
scanner := bufio.NewScanner(stdout)
err = cmd.Start()
if err != nil {
panic(err)
}
for scanner.Scan() {
fmt.Println(scanner.Text())
text := scanner.Text()
fmt.Println(text)
}
if scanner.Err() != nil {
cmd.Process.Kill()
cmd.Wait()
panic(scanner.Err())
}
// This also needs to be executed in root and non root variants
fmt.Println("Reading cloudflared folder to get the JSON file.")
isRoot := false
dir := "/home/system/.cloudflared/"
dir2 := "/root/.cloudflared/"
files, err := os.ReadDir(dir)
if err != nil {
panic(err)
}
var jsonFile os.DirEntry
for _, file := range files {
// check if file has json extension
if filepath.Ext(file.Name()) == ".json" {
fmt.Println("Non-Root JSON file found: " + file.Name())
jsonFile = file
}
}
// If the files are not in the home folder, try the root folder
if jsonFile == nil {
files, err = os.ReadDir(dir2)
if err != nil {
panic(err)
}
for _, file := range files {
// check if file has json extension
if filepath.Ext(file.Name()) == ".json" {
fmt.Println("Root JSON file found: " + file.Name())
jsonFile = file
isRoot = true
}
}
}
if jsonFile == nil {
panic("No JSON file found in directory")
}
fmt.Println("Reading JSON file.")
targetDir := "/home/system/.cloudflared/"
if isRoot {
targetDir = "/root/.cloudflared/"
}
jsonFilePath := filepath.Join(targetDir, jsonFile.Name())
jsonBytes, err := ioutil.ReadFile(jsonFilePath)
if err != nil {
panic(err)
}
fmt.Println("Parsing JSON file.")
var data cloudflaredTunnelJson
err = json.Unmarshal(jsonBytes, &data)
if err != nil {
log.Printf("Error reading tunnel JSON file: %s", err)
}
fmt.Println("Tunnel ID is:" + data.TunnelID)
// create the config.yml file with the following content in each line:
// "url": "http://localhost:80"
// "tunnel": "<TunnelID>"
// "credentials-file": "/root/.cloudflared/<tunnelID>.json"
file := configDestination
f, err := os.Create(file)
if err != nil {
panic(err)
}
defer f.Close()
_, err = f.WriteString("url: http://localhost:80\ntunnel: " + data.TunnelID + "\ncredentials-file: " + jsonFilePath)
if err != nil {
panic(err)
}
}
// DeleteTunnel: Deletes a tunnel via cloudflared, this does not remove the service
func DeleteTunnel() {
fmt.Println("Deleting possible previous tunnel.")
// Configure the service and start it
cmd := exec.Command("sh", "/home/system/components/edgeboxctl/scripts/cloudflared_tunnel_delete.sh")
stdout, err := cmd.StdoutPipe()
if err != nil {
panic(err)
}
scanner := bufio.NewScanner(stdout)
err = cmd.Start()
if err != nil {
panic(err)
}
for scanner.Scan() {
fmt.Println(scanner.Text())
text := scanner.Text()
fmt.Println(text)
}
if scanner.Err() != nil {
cmd.Process.Kill()
cmd.Wait()
panic(scanner.Err())
}
}
// InstallTunnelService: Installs the tunnel service
func InstallTunnelService(config string) {
fmt.Println("Installing cloudflared service.")
cmd := exec.Command("cloudflared", "--config", config, "service", "install")
cmd.Start()
cmd.Wait()
}
// RemoveTunnelService: Removes the tunnel service
func RemoveTunnelService() {
wsPath := utils.GetPath(utils.WsPath)
fmt.Println("Removing possibly previous service install.")
cmd := exec.Command("cloudflared", "service", "uninstall")
cmd.Start()
cmd.Wait()
fmt.Println("Removing cloudflared files")
cmdargs := []string{"-rf", "/home/system/.cloudflared"}
utils.Exec(wsPath, "rm", cmdargs)
cmdargs = []string{"-rf", "/etc/cloudflared/config.yml"}
utils.Exec(wsPath, "rm", cmdargs)
cmdargs = []string{"-rf", "/root/.cloudflared/cert.pem"}
utils.Exec(wsPath, "rm", cmdargs)
}
func CopyDir(src string, dest string) error {
srcInfo, err := os.Stat(src)
if err != nil {
return err
}
if !srcInfo.IsDir() {
return fmt.Errorf("%s is not a directory", src)
}
err = os.MkdirAll(dest, srcInfo.Mode())
if err != nil {
return err
}
items, err := ioutil.ReadDir(src)
if err != nil {
return err
}
for _, item := range items {
srcPath := filepath.Join(src, item.Name())
destPath := filepath.Join(dest, item.Name())
if item.IsDir() {
err = CopyDir(srcPath, destPath)
if err != nil {
fmt.Printf("error copying directory %s to %s: %s\n", srcPath, destPath, err.Error())
}
} else {
err = CopyFile(srcPath, destPath)
if err != nil {
fmt.Printf("error copying file %s to %s: %s\n", srcPath, destPath, err.Error())
}
}
}
return nil
}
func CopyFile(src string, dest string) error {
srcFile, err := os.Open(src)
if err != nil {
return err
}
defer srcFile.Close()
destFile, err := os.Create(dest)
if err != nil {
return err
}
defer destFile.Close()
_, err = io.Copy(destFile, srcFile)
if err != nil {
return err
}
err = destFile.Sync()
if err != nil {
return err
}
srcInfo, err := os.Stat(src)
if err != nil {
return err
}
err = os.Chmod(dest, srcInfo.Mode())
if err != nil {
return err
}
return nil
}
func CheckUpdates() {
fmt.Println("Checking for Edgebox System Updates.")
// Configure the service and start it
cmd := exec.Command("sh", "/home/system/components/updater/run.sh", "--check")
stdout, err := cmd.StdoutPipe()
if err != nil {
panic(err)
}
scanner := bufio.NewScanner(stdout)
err = cmd.Start()
if err != nil {
panic(err)
}
for scanner.Scan() {
// fmt.Println(scanner.Text())
text := scanner.Text()
fmt.Println(text)
}
if scanner.Err() != nil {
cmd.Process.Kill()
cmd.Wait()
fmt.Println("Error running updates check.")
utils.WriteOption("SYSTEM_UPDATES", "[]")
return
}
// Read targets.env file into JSON list structure
targets := []string{}
targetsFile, err := os.Open("/home/system/components/updater/targets.env")
if err != nil {
fmt.Println("No targets.env file found. Skipping.")
utils.WriteOption("SYSTEM_UPDATES", "[]")
return
}
defer targetsFile.Close()
scanner = bufio.NewScanner(targetsFile)
for scanner.Scan() {
text := scanner.Text()
// text line should look like: {"target": "<target>", "version": "<version>"}
target := strings.Split(text, "=")
newText := "{\"target\": \"" + strings.Replace(target[0], "_VERSION", "", -1) + "\", \"version\": \"" + target[1] + "\"}"
targets = append(targets, newText)
}
if scanner.Err() != nil {
fmt.Println("Error reading update targets file.")
utils.WriteOption("SYSTEM_UPDATES", "[]")
return
}
// convert targets to string
targetsString := strings.Join(targets, ",")
targetsString = "[" + targetsString + "]"
fmt.Println(targetsString)
// Write option with targets
utils.WriteOption("SYSTEM_UPDATES", targetsString)
}
func ApplyUpdates() {
fmt.Println("Applying Edgebox System Updates.")
utils.WriteOption("UPDATING_SYSTEM", "true")
// Configure the service and start it
cmd := exec.Command("sh", "/home/system/components/updater/run.sh", "--update")
stdout, err := cmd.StdoutPipe()
if err != nil {
panic(err)
}
scanner := bufio.NewScanner(stdout)
err = cmd.Start()
if err != nil {
panic(err)
}
for scanner.Scan() {
fmt.Println(scanner.Text())
text := scanner.Text()
fmt.Println(text)
}
if scanner.Err() != nil {
cmd.Process.Kill()
cmd.Wait()
panic(scanner.Err())
}
// If the system did not yet restart, set updating system to false
utils.WriteOption("UPDATING_SYSTEM", "false")
}
func FetchBrowserDevPasswordFromFile() (string, error) {
fmt.Println("Executing FetchBrowserDevPasswordFromFile")
// Read the "password" entry on the yaml file
// Read the yaml file in system.GetPath(BrowserDevPasswordFileLocation)
yamlFile, err := ioutil.ReadFile(utils.GetPath(utils.BrowserDevPasswordFileLocation))
if err != nil {
return "", err
}
// Parse the yaml file and get the "password" entry
var yamlFileMap yaml.MapSlice
err = yaml.Unmarshal(yamlFile, &yamlFileMap)
if err != nil {
return "", err
}
for _, item := range yamlFileMap {
key, value := item.Key, item.Value
if key == "password" {
if pwString, ok := value.(string); ok {
return pwString, nil
} else {
return "", errors.New("password value is not a string")
}
}
}
return "", errors.New("password key not found")
}
func SetBrowserDevPasswordFile(password string) error {
// Get current password from file
currentPassword, err := FetchBrowserDevPasswordFromFile()
if err != nil {
fmt.Println("Error fetching current password from file.")
return err
}
// Write the new password on the file using ReplaceTextInFile
err = ReplaceTextInFile(utils.GetPath(utils.BrowserDevPasswordFileLocation), currentPassword, password)
if err != nil {
fmt.Println("Error writing new password to file.")
return err
}
return nil
}
func ReplaceTextInFile(filePath string, oldText string, newText string) error {
// Open the file for reading
file, err := os.OpenFile(filePath, os.O_RDWR, 0644)
if err != nil {
return err
}
// Read the file contents
data, err := ioutil.ReadAll(file)
if err != nil {
return err
}
// Close the file
err = file.Close()
if err != nil {
return err
}
// Replace the text in the file
newData := strings.Replace(string(data), oldText, newText, -1)
// Write the new data back to the file
err = ioutil.WriteFile(filePath, []byte(newData), 0644)
if err != nil {
return err
}
return nil
}
-217
View File
@@ -1,217 +0,0 @@
package tasks
import (
"crypto/sha256"
"encoding/base64"
"encoding/binary"
"encoding/json"
"fmt"
"os"
"os/exec"
"path/filepath"
"strings"
"github.com/edgebox-iot/edgeboxctl/internal/utils"
)
const (
sshDirectory = "/root/.ssh"
managedSSHComment = "edgebox-dashboard-managed"
)
type sshAccessResult struct {
Status string `json:"status"`
PublicKey string `json:"public_key,omitempty"`
Fingerprint string `json:"fingerprint,omitempty"`
}
func taskEnableSSHAccess(args taskEnableSSHAccessArgs) (string, error) {
if err := verifyRootSSHAccess(); err != nil {
return "", err
}
publicKey, fingerprint, err := parseSSHEd25519PublicKey(args.PublicKey)
if err != nil {
return "", err
}
if err := reconcileManagedSSHKey(sshDirectory, publicKey); err != nil {
return "", err
}
utils.WriteOption("SSH_ACCESS_ENABLED", "true")
utils.WriteOption("SSH_PUBLIC_KEY", publicKey)
utils.WriteOption("SSH_KEY_FINGERPRINT", fingerprint)
result, err := json.Marshal(sshAccessResult{
Status: "enabled",
PublicKey: publicKey,
Fingerprint: fingerprint,
})
if err != nil {
return "", err
}
return string(result), nil
}
func taskDisableSSHAccess() (string, error) {
if err := reconcileManagedSSHKey(sshDirectory, ""); err != nil {
return "", err
}
utils.WriteOption("SSH_ACCESS_ENABLED", "false")
utils.DeleteOption("SSH_PUBLIC_KEY")
utils.DeleteOption("SSH_KEY_FINGERPRINT")
result, err := json.Marshal(sshAccessResult{Status: "disabled"})
if err != nil {
return "", err
}
return string(result), nil
}
func parseSSHEd25519PublicKey(input string) (string, string, error) {
trimmed := strings.TrimSpace(input)
if trimmed == "" || strings.ContainsAny(trimmed, "\r\n") {
return "", "", fmt.Errorf("provide exactly one SSH public key")
}
fields := strings.Fields(trimmed)
if len(fields) < 2 || len(fields) > 3 || fields[0] != "ssh-ed25519" {
return "", "", fmt.Errorf("only one ssh-ed25519 public key is supported")
}
keyBlob, err := base64.StdEncoding.DecodeString(fields[1])
if err != nil || !validEd25519KeyBlob(keyBlob) {
return "", "", fmt.Errorf("invalid ssh-ed25519 public key")
}
digest := sha256.Sum256(keyBlob)
publicKey := "ssh-ed25519 " + base64.StdEncoding.EncodeToString(keyBlob) + " " + managedSSHComment
fingerprint := "SHA256:" + base64.RawStdEncoding.EncodeToString(digest[:])
return publicKey, fingerprint, nil
}
func validEd25519KeyBlob(blob []byte) bool {
if len(blob) < 4 {
return false
}
typeLength := int(binary.BigEndian.Uint32(blob[:4]))
if typeLength != len("ssh-ed25519") || len(blob) < 4+typeLength+4 {
return false
}
if string(blob[4:4+typeLength]) != "ssh-ed25519" {
return false
}
keyLengthOffset := 4 + typeLength
keyLength := int(binary.BigEndian.Uint32(blob[keyLengthOffset : keyLengthOffset+4]))
return keyLength == 32 && len(blob) == keyLengthOffset+4+keyLength
}
func reconcileManagedSSHKey(directory string, publicKey string) error {
if info, err := os.Lstat(directory); err == nil {
if info.Mode()&os.ModeSymlink != 0 || !info.IsDir() {
return fmt.Errorf("SSH directory is not a regular directory")
}
} else if !os.IsNotExist(err) {
return fmt.Errorf("inspect SSH directory: %w", err)
} else if err := os.MkdirAll(directory, 0700); err != nil {
return fmt.Errorf("create SSH directory: %w", err)
}
if err := os.Chmod(directory, 0700); err != nil {
return fmt.Errorf("secure SSH directory: %w", err)
}
authorizedKeysPath := filepath.Join(directory, "authorized_keys")
if info, err := os.Lstat(authorizedKeysPath); err == nil && info.Mode()&os.ModeSymlink != 0 {
return fmt.Errorf("authorized_keys must not be a symbolic link")
} else if err != nil && !os.IsNotExist(err) {
return fmt.Errorf("inspect authorized_keys: %w", err)
}
existing, err := os.ReadFile(authorizedKeysPath)
if err != nil && !os.IsNotExist(err) {
return fmt.Errorf("read authorized_keys: %w", err)
}
lines := strings.Split(string(existing), "\n")
kept := make([]string, 0, len(lines)+1)
for _, line := range lines {
if strings.TrimSpace(line) == "" || isManagedSSHKey(line) {
continue
}
kept = append(kept, line)
}
if publicKey != "" {
kept = append(kept, publicKey)
}
contents := ""
if len(kept) > 0 {
contents = strings.Join(kept, "\n") + "\n"
}
temporary, err := os.CreateTemp(directory, ".authorized_keys-*")
if err != nil {
return fmt.Errorf("create authorized_keys temporary file: %w", err)
}
temporaryPath := temporary.Name()
defer os.Remove(temporaryPath)
if err := temporary.Chmod(0600); err != nil {
temporary.Close()
return fmt.Errorf("secure authorized_keys temporary file: %w", err)
}
if _, err := temporary.WriteString(contents); err != nil {
temporary.Close()
return fmt.Errorf("write authorized_keys: %w", err)
}
if err := temporary.Sync(); err != nil {
temporary.Close()
return fmt.Errorf("sync authorized_keys: %w", err)
}
if err := temporary.Close(); err != nil {
return fmt.Errorf("close authorized_keys: %w", err)
}
if err := os.Rename(temporaryPath, authorizedKeysPath); err != nil {
return fmt.Errorf("replace authorized_keys: %w", err)
}
return nil
}
func isManagedSSHKey(line string) bool {
fields := strings.Fields(line)
return len(fields) == 3 && fields[2] == managedSSHComment
}
func verifyRootSSHAccess() error {
sshdPath, err := exec.LookPath("sshd")
if err != nil {
sshdPath = "/usr/sbin/sshd"
if _, statErr := os.Stat(sshdPath); statErr != nil {
return fmt.Errorf("OpenSSH server is not installed")
}
}
output, err := exec.Command(sshdPath, "-T").Output()
if err != nil {
return fmt.Errorf("could not verify the effective SSH server configuration")
}
settings := string(output)
if !strings.Contains(settings, "pubkeyauthentication yes") {
return fmt.Errorf("SSH public-key authentication is disabled")
}
if strings.Contains(settings, "permitrootlogin no") {
return fmt.Errorf("SSH root login is disabled")
}
return nil
}
-111
View File
@@ -1,111 +0,0 @@
package tasks
import (
"encoding/base64"
"encoding/binary"
"os"
"path/filepath"
"strings"
"testing"
)
func testPublicKey(t *testing.T) string {
t.Helper()
blob := make([]byte, 4+len("ssh-ed25519")+4+32)
binary.BigEndian.PutUint32(blob[:4], uint32(len("ssh-ed25519")))
copy(blob[4:], "ssh-ed25519")
offset := 4 + len("ssh-ed25519")
binary.BigEndian.PutUint32(blob[offset:offset+4], 32)
for index := 0; index < 32; index++ {
blob[offset+4+index] = byte(index + 1)
}
return "ssh-ed25519 " + base64.StdEncoding.EncodeToString(blob) + " workstation"
}
func TestParseSSHEd25519PublicKey(t *testing.T) {
publicKey, fingerprint, err := parseSSHEd25519PublicKey(testPublicKey(t))
if err != nil {
t.Fatal(err)
}
if !strings.HasSuffix(publicKey, " "+managedSSHComment) {
t.Fatalf("public key does not have managed marker: %q", publicKey)
}
if !strings.HasPrefix(fingerprint, "SHA256:") {
t.Fatalf("unexpected fingerprint: %q", fingerprint)
}
}
func TestParseSSHEd25519PublicKeyRejectsUnsafeInput(t *testing.T) {
inputs := []string{
"",
"-----BEGIN OPENSSH PRIVATE KEY-----",
testPublicKey(t) + "\n" + testPublicKey(t),
"command=whoami " + testPublicKey(t),
"ssh-rsa AAAA invalid",
}
for _, input := range inputs {
if _, _, err := parseSSHEd25519PublicKey(input); err == nil {
t.Fatalf("expected input to be rejected: %q", input)
}
}
}
func TestReconcileManagedSSHKeyPreservesUnrelatedKeys(t *testing.T) {
directory := t.TempDir()
authorizedKeysPath := filepath.Join(directory, "authorized_keys")
original := "# operator key\nssh-ed25519 AAAAoperator operator\n"
if err := os.WriteFile(authorizedKeysPath, []byte(original), 0644); err != nil {
t.Fatal(err)
}
publicKey, _, err := parseSSHEd25519PublicKey(testPublicKey(t))
if err != nil {
t.Fatal(err)
}
if err := reconcileManagedSSHKey(directory, publicKey); err != nil {
t.Fatal(err)
}
if err := reconcileManagedSSHKey(directory, publicKey); err != nil {
t.Fatal(err)
}
contents, err := os.ReadFile(authorizedKeysPath)
if err != nil {
t.Fatal(err)
}
if strings.Count(string(contents), managedSSHComment) != 1 {
t.Fatalf("managed key is not idempotent: %s", contents)
}
if !strings.Contains(string(contents), original) {
t.Fatalf("unrelated content was changed: %s", contents)
}
if info, err := os.Stat(authorizedKeysPath); err != nil || info.Mode().Perm() != 0600 {
t.Fatalf("authorized_keys mode is not 0600: %v, %v", info, err)
}
if err := reconcileManagedSSHKey(directory, ""); err != nil {
t.Fatal(err)
}
contents, err = os.ReadFile(authorizedKeysPath)
if err != nil {
t.Fatal(err)
}
if string(contents) != original {
t.Fatalf("disable changed unrelated content: %q", contents)
}
}
func TestReconcileManagedSSHKeyRejectsSymlink(t *testing.T) {
directory := t.TempDir()
target := filepath.Join(directory, "target")
if err := os.WriteFile(target, []byte("preserve me"), 0600); err != nil {
t.Fatal(err)
}
if err := os.Symlink(target, filepath.Join(directory, "authorized_keys")); err != nil {
t.Fatal(err)
}
if err := reconcileManagedSSHKey(directory, ""); err == nil {
t.Fatal("expected symlink to be rejected")
}
}
+56 -1160
View File
File diff suppressed because it is too large Load Diff
+29 -114
View File
@@ -4,6 +4,7 @@ import (
"bufio"
"bytes"
"database/sql"
"encoding/json"
"fmt"
"io"
"log"
@@ -12,11 +13,12 @@ import (
"strings"
"time"
"github.com/TylerBrock/colorjson"
"github.com/joho/godotenv"
)
// ExecAndStream : Runs a terminal command, but streams progress instead of outputting. Ideal for long lived process that need to be logged.
func ExecAndStream(path string, command string, args []string) string {
func ExecAndStream(path string, command string, args []string) {
cmd := exec.Command(command, args...)
@@ -27,17 +29,13 @@ func ExecAndStream(path string, command string, args []string) string {
err := cmd.Run()
outStr, errStr := string(stdoutBuf.Bytes()), string(stderrBuf.Bytes())
returnVal := outStr
if err != nil {
fmt.Printf("cmd.Run() failed with %s\n", err)
returnVal = errStr
}
outStr, errStr := string(stdoutBuf.Bytes()), string(stderrBuf.Bytes())
fmt.Printf("\nout:\n%s\nerr:\n%s\n", outStr, errStr)
return returnVal
}
// Exec : Runs a terminal Command, catches and logs errors, returns the result.
@@ -85,7 +83,7 @@ func DeleteEmptySlices(s []string) []string {
func GetSQLiteDbConnectionDetails() string {
var apiEnv map[string]string
apiEnv, err := godotenv.Read(GetPath(ApiEnvFileLocation))
apiEnv, err := godotenv.Read(GetPath("apiEnvFileLocation"))
if err != nil {
log.Fatal("Error loading .env file")
@@ -95,8 +93,8 @@ func GetSQLiteDbConnectionDetails() string {
}
// GetSQLiteFormattedDateTime: Given a Time, Returns a string that is formatted ready to be inserted into an SQLite Datetime field using sql.Prepare.
func GetSQLiteFormattedDateTime(t time.Time) string {
// GetFormattedDateTime: Given a Time, Returns a string that is formatted to be passed around the application, including correctly inserting SQLite Datetime field using sql.Prepare.
func GetFormattedDateTime(t time.Time) string {
// This date is used to indicate the layout.
const datetimeLayout = "2006-01-02 15:04:05"
formatedDatetime := t.Format(datetimeLayout)
@@ -104,19 +102,6 @@ func GetSQLiteFormattedDateTime(t time.Time) string {
return formatedDatetime
}
const BackupPasswordFileLocation string = "backupPasswordFileLocation"
const CloudEnvFileLocation string = "cloudEnvFileLocation"
const ApiEnvFileLocation string = "apiEnvFileLocation"
const ApiPath string = "apiPath"
const EdgeAppsPath string = "edgeAppsPath"
const EdgeAppsBackupPath string = "edgeAppsBackupPath"
const WsPath string = "wsPath"
const BrowserDevPath string = "browserDevPath"
const LoggerPath string = "loggerPath"
const BrowserDevPasswordFileLocation string = "browserDevPasswordFileLocation"
const BrowserDevProxyPath string = "browserDevProxyPath"
// 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 {
@@ -130,15 +115,15 @@ func GetPath(pathKey string) string {
}
switch pathKey {
case CloudEnvFileLocation:
case "cloudEnvFileLocation":
if env["CLOUD_ENV_FILE_LOCATION"] != "" {
targetPath = env["CLOUD_ENV_FILE_LOCATION"]
} else {
targetPath = "/home/system/components/api/cloud.env"
targetPath = "/home/system/components/edgeboxctl/cloud.env"
}
case ApiEnvFileLocation:
case "apiEnvFileLocation":
if env["API_ENV_FILE_LOCATION"] != "" {
targetPath = env["API_ENV_FILE_LOCATION"]
@@ -146,15 +131,15 @@ func GetPath(pathKey string) string {
targetPath = "/home/system/components/api/edgebox.env"
}
case ApiPath:
case "apiPath":
if env["API_PATH"] != "" {
targetPath = env["API_PATH"]
targetPath = env["APT_PATH"]
} else {
targetPath = "/home/system/components/api/"
}
case EdgeAppsPath:
case "edgeAppsPath":
if env["EDGEAPPS_PATH"] != "" {
targetPath = env["EDGEAPPS_PATH"]
@@ -162,14 +147,7 @@ func GetPath(pathKey string) string {
targetPath = "/home/system/components/apps/"
}
case EdgeAppsBackupPath:
if env["EDGEAPPS_BACKUP_PATH"] != "" {
targetPath = env["EDGEAPPS_BACKUP_PATH"]
} else {
targetPath = "/home/system/components/backups/"
}
case WsPath:
case "wsPath":
if env["WS_PATH"] != "" {
targetPath = env["WS_PATH"]
@@ -177,43 +155,6 @@ func GetPath(pathKey string) string {
targetPath = "/home/system/components/ws/"
}
case BrowserDevPath:
if env["BROWSERDEV_PATH"] != "" {
targetPath = env["BROWSERDEV_PATH"]
} else {
targetPath = "/home/system/components/dev/"
}
case LoggerPath:
if env["LOGGER_PATH"] != "" {
targetPath = env["LOGGER_PATH"]
} else {
targetPath = "/home/system/components/logger/"
}
case BackupPasswordFileLocation:
if env["BACKUP_PASSWORD_FILE_LOCATION"] != "" {
targetPath = env["BACKUP_PASSWORD_FILE_LOCATION"]
} else {
targetPath = "/home/system/components/backups/pw.txt"
}
case BrowserDevPasswordFileLocation:
if env["BROWSERDEV_PASSWORD_FILE_LOCATION"] != "" {
targetPath = env["BROWSERDEV_PASSWORD_FILE_LOCATION"]
} else {
targetPath = "/root/.config/code-server/config.yaml"
}
case BrowserDevProxyPath:
if env["BROWSERDEV_PROXY_PATH"] != "" {
targetPath = env["BROWSERDEV_PROXY_PATH"]
} else {
targetPath = "/home/system/components/dev/"
}
default:
log.Printf("path_key %s nonexistant in GetPath().\n", pathKey)
@@ -238,7 +179,7 @@ func WriteOption(optionKey string, optionValue string) {
log.Fatal(err.Error())
}
formatedDatetime := GetSQLiteFormattedDateTime(time.Now())
formatedDatetime := GetFormattedDateTime(time.Now())
_, err = statement.Exec(optionKey, optionValue, formatedDatetime, formatedDatetime) // Execute SQL Statement
if err != nil {
@@ -248,46 +189,20 @@ func WriteOption(optionKey string, optionValue string) {
db.Close()
}
// ReadOption : Reads a key value pair option from the api shared database
func ReadOption(optionKey string) string {
db, err := sql.Open("sqlite3", GetSQLiteDbConnectionDetails())
if err != nil {
log.Fatal(err.Error())
}
var optionValue string
err = db.QueryRow("SELECT value FROM option WHERE name = ?", optionKey).Scan(&optionValue)
if err != nil {
log.Println(err.Error())
}
db.Close()
return optionValue
// IsSystemReady : Checks readiness of the service to execute commands (Only after "edgebox --build" is ran at least once via SSH, or if built for distribution)
func IsSystemReady() bool {
_, err := os.Stat(GetPath("wsPath") + ".ready")
return !os.IsNotExist(err)
}
// DeleteOption : Deletes a key value pair option from the api shared database
func DeleteOption(optionKey string) {
db, err := sql.Open("sqlite3", GetSQLiteDbConnectionDetails())
if err != nil {
log.Fatal(err.Error())
}
statement, err := db.Prepare("DELETE FROM option WHERE name = ?;") // Prepare SQL Statement
if err != nil {
log.Fatal(err.Error())
}
_, err = statement.Exec(optionKey) // Execute SQL Statement
if err != nil {
log.Fatal(err.Error())
}
db.Close()
// IsDatabaseReady : Checks is it can successfully connect to the task queue db
func IsDatabaseReady() bool {
return false
}
func ColorJsonString(jsonString string) string {
var obj map[string]interface{}
json.Unmarshal([]byte(jsonString), &obj)
resultJSON, _ := colorjson.Marshal(obj)
return string(resultJSON)
}
+4 -3
View File
@@ -89,16 +89,17 @@ func TestGetPath(t *testing.T) {
t.Fail()
}
result = GetPath(WsPath)
validPathKey := "wsPath"
result = GetPath(validPathKey)
if result != "/home/system/components/ws/" {
t.Log("Expected /home/system/components/ws/ but got", result)
t.Fail()
}
}
func TestGetSQLiteFormattedDateTime(t *testing.T) {
func TestGetFormattedDateTime(t *testing.T) {
datetime := time.Date(2021, time.Month(1), 01, 1, 30, 15, 0, time.UTC)
result := GetSQLiteFormattedDateTime(datetime)
result := GetFormattedDateTime(datetime)
if result != "2021-01-01 01:30:15" {
t.Log("Expected 2021-01-01 01:30:15 but got ", result)
-6
View File
@@ -1,6 +0,0 @@
#!/usr/bin/env sh
echo "Starting script login"
cloudflared tunnel login 2>&1 | tee /home/system/components/edgeboxctl/scripts/output.log &
echo "sleeping 5 seconds"
sleep 5
-7
View File
@@ -1,7 +0,0 @@
#!/usr/bin/env sh
echo "Starting script create"
# script -q -c "cloudflared tunnel login 2>&1 | tee /app/output.log" &
cloudflared tunnel create edgebox 2>&1 | tee /home/system/components/edgeboxctl/scripts/output.log
echo "sleeping 5 seconds"
sleep 5
-7
View File
@@ -1,7 +0,0 @@
#!/usr/bin/env sh
echo "Starting script delete"
TUNNEL_ORIGIN_CERT=/home/system/.cloudflared/cert.pem
cloudflared tunnel delete edgebox 2>&1 | tee /home/system/components/edgeboxctl/scripts/output.log &
echo "sleeping 5 seconds"
sleep 5