36 Commits
Author SHA1 Message Date
Paulo Truta 80fc8b40cd Remove postinstall flagged file when removing app 2024-11-03 03:25:59 +01:00
Paulo Truta 01f423ee84 Add support to fetch experimental info from apps, add bulk install edgeapps task 2024-11-02 22:42:56 +01:00
Paulo Truta a8b1da4b7c Added update_system task and accompanying logic (#37) 2024-07-04 16:22:32 +02:00
Paulo Truta 6ccd4a3299 Excluding tiny storage devices 2024-04-21 00:46:30 +02:00
Paulo Truta 42b1a34ca7 Fix runnable file check 2024-04-17 20:48:53 +02:00
Paulo Truta 4bb343769f Faster GetEdgeAppServices by skipping ones that should not be running 2024-04-17 20:40:00 +02:00
Paulo Trutaand= <=> 3bb6200b1c Added args and tasks to start and stop the sshx shell (#35)
Co-authored-by: = <=>
2024-04-17 20:18:13 +02:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> eae72b0a79 Bump actions/checkout from 3 to 4 (#34)
Bumps [actions/checkout](https://github.com/actions/checkout) from 3 to 4.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-11-20 21:10:09 +01:00
Malachi Soord 8be566afb9 Fix up install 2023-11-19 21:46:11 +01:00
Malachi Soord 9c2a0dbefe Tidy repo (#30)
* Tidy repo

* Tidy

* Fix up

* Setup default target
2023-11-19 21:33:18 +01:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 4e399f63ea Bump actions/setup-go from 3 to 4 (#31)
Bumps [actions/setup-go](https://github.com/actions/setup-go) from 3 to 4.
- [Release notes](https://github.com/actions/setup-go/releases)
- [Commits](https://github.com/actions/setup-go/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/setup-go
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-11-19 12:31:08 +01:00
Paulo Truta e238e9d71d Basic Auth and RemoveApp Support 2023-11-14 21:00:09 +01:00
= db56530311 Don't fail install if service cannot be stopped (does not exist yet) 2023-11-05 12:19:09 +01:00
= 319acbd0e1 Add explicit support for prod amd64 build in Makefile 2023-11-05 11:47:42 +01:00
Paulo Truta f0047a7a0c added cluster_ssh_port_var to read from env 2023-11-05 02:03:46 +01:00
Paulo Truta 5c736a1b85 Adjustments to cloud options, env location and expected vars 2023-11-05 02:01:16 +01:00
= ca7f4af7fe Added Edgeapps Options Feature 2023-10-29 20:48:14 +01:00
Paulo Truta 445bc41d0e Fixes to install make command, fixes to local network app url definition, run ws build on start and every 24 hours 2023-10-28 18:43:18 +02:00
= 8bf26a334c Added system.CopyDir and EdgeappsBackupPath conf 2023-06-10 17:29:54 +02:00
Paulo Truta e009c1f55d NetworkURL based on hostname 2023-06-04 23:19:34 +02:00
Paulo Truta 284acb18a4 Added restore functionality 2023-05-31 00:03:23 +00:00
Paulo Truta 8d0de36cdd Removed err from log 2023-05-30 21:25:40 +02:00
Paulo Truta 113785def4 Using Sprintf to convert from int64 to string of digits 2023-05-30 21:23:33 +02:00
Paulo Truta df13bf705d Removed formatting directive from Println call 2023-05-30 21:22:31 +02:00
Paulo Truta 335b7ec29e Commented UsageStat member of Repository 2023-05-30 21:21:24 +02:00
Paulo Truta 13275f80a5 Temporarily commented imports on backups module 2023-05-30 21:20:30 +02:00
Paulo Truta 04cdba7479 Run next backup based on time elapsed since last backup record 2023-05-30 20:38:06 +02:00
Paulo Truta a66ff40e65 Backups feature 2023-05-29 21:52:27 +02:00
Paulo Truta f7823b9e20 Added make build-<arch> commands 2023-04-17 20:28:06 +02:00
Paulo Truta ece500c342 Cloudflare Tunnel Support (#29)
* Adding necessary changes for tunnel setup supporting cloudflared

* Applied shwrap pattern to login, create and delete tunnel actions, many fixes

* Refactoring sesh 1

* Added utils, remaining tunnel tasks, cleanup
2023-03-20 00:54:42 +01:00
Paulo Truta 161ec76eb9 Add service file and install command (#27)
* Added systemd service file

* Added make install command (copies binary to local bin and adds service file

* Makefile has intall-prod and install-cloud commands
2023-03-11 13:58:54 +01:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> d9720a48c9 Bump actions/setup-go from 2 to 3 (#24)
Bumps [actions/setup-go](https://github.com/actions/setup-go) from 2 to 3.
- [Release notes](https://github.com/actions/setup-go/releases)
- [Commits](https://github.com/actions/setup-go/compare/v2...v3)

---
updated-dependencies:
- dependency-name: actions/setup-go
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-09 20:12:49 +01:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 1ba5a8f506 Bump actions/checkout from 2 to 3 (#23)
Bumps [actions/checkout](https://github.com/actions/checkout) from 2 to 3.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v2...v3)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-09 20:12:41 +01:00
Malachi Soord 25d5ca74f8 Create dependabot.yml (#22) 2023-03-09 19:28:58 +01:00
Paulo Truta d62cb57222 Update README.md 2023-03-05 13:46:38 +01:00
Malachi Soord 0680b7ffb9 GetPath improvements (#20)
* Use named variables for getting path
Fixed bug with env var

* Fix test

* Use utils to get paths
2022-10-08 18:41:37 +02:00
18 changed files with 1989 additions and 146 deletions
+10
View File
@@ -0,0 +1,10 @@
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 runs-on: ubuntu-latest
steps: steps:
- name: Set up Go - name: Set up Go
uses: actions/setup-go@v2 uses: actions/setup-go@v4
with: with:
go-version: ^1.15 go-version: '1.20.2'
- name: Check out code - name: Check out code
uses: actions/checkout@v2 uses: actions/checkout@v4
- name: Build - name: Build
run: make build run: make build
- name: Test - name: Test
+3
View File
@@ -21,3 +21,6 @@ main
# Build # Build
/bin /bin
# Logs from executed scripts
/scripts/output.log
+3 -5
View File
@@ -1,11 +1,9 @@
FROM golang:latest FROM golang:1.20.2
WORKDIR /app WORKDIR /app
COPY ./ /app COPY ./ /app
RUN go mod download RUN go install github.com/githubnemo/CompileDaemon@latest
RUN go get github.com/githubnemo/CompileDaemon ENTRYPOINT CompileDaemon --build="make build" --command=./bin/edgeboxctl-$(go env GOOS)-$(go env GOARCH)
ENTRYPOINT CompileDaemon --build="make build" --command=./bin/edgeboxctl
+43 -2
View File
@@ -1,9 +1,13 @@
PROJECT?=github.com/edgebox-iot/edgeboxctl .DEFAULT_GOAL := build
PROJECT ?= github.com/edgebox-iot/edgeboxctl
RELEASE ?= dev RELEASE ?= dev
COMMIT := $(shell git rev-parse --short HEAD) COMMIT := $(shell git rev-parse --short HEAD)
BUILD_DATE := $(shell date -u '+%Y-%m-%d_%H:%M:%S') BUILD_DATE := $(shell date -u '+%Y-%m-%d_%H:%M:%S')
BUILD_DIR = bin BUILD_DIR = bin
GOOS := $(shell go env GOOS)
GOARCH := $(shell go env GOARCH)
build-all: build-all:
GOOS=linux GOARCH=amd64 make build GOOS=linux GOARCH=amd64 make build
@@ -15,6 +19,16 @@ build-prod:
build-cloud: build-cloud:
GOOS=linux GOARCH=amd64 RELEASE=cloud make build 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
build: build:
@echo "Building ${GOOS}-${GOARCH}" @echo "Building ${GOOS}-${GOARCH}"
GOOS=${GOOS} GOARCH=${GOARCH} go build \ GOOS=${GOOS} GOARCH=${GOARCH} go build \
@@ -22,7 +36,6 @@ build:
-X ${PROJECT}/internal/diagnostics.Commit=${COMMIT} \ -X ${PROJECT}/internal/diagnostics.Commit=${COMMIT} \
-X ${PROJECT}/internal/diagnostics.BuildDate=${BUILD_DATE}" \ -X ${PROJECT}/internal/diagnostics.BuildDate=${BUILD_DATE}" \
-o bin/edgeboxctl-${GOOS}-${GOARCH} ${PROJECT}/cmd/edgeboxctl -o bin/edgeboxctl-${GOOS}-${GOARCH} ${PROJECT}/cmd/edgeboxctl
cp ./bin/edgeboxctl-${GOOS}-${GOARCH} ./bin/edgeboxctl
clean: clean:
rm -rf ${BUILD_DIR} rm -rf ${BUILD_DIR}
@@ -33,3 +46,31 @@ test:
test-with-coverage: test-with-coverage:
go test -tags=unit -timeout=600s -v ./... -coverprofile=coverage.out go test -tags=unit -timeout=600s -v ./... -coverprofile=coverage.out
install:
sudo systemctl stop edgeboxctl || true
sudo rm -rf /usr/local/bin/edgeboxctl /usr/local/sbin/edgeboctl /lib/systemd/system/edgeboxctl.service
sudo cp ./bin/edgeboxctl-${GOOS}-${GOARCH} /usr/local/bin/edgeboxctl
sudo cp ./bin/edgeboxctl-${GOOS}-${GOARCH} /usr/local/sbin/edgeboxctl
sudo cp ./edgeboxctl.service /lib/systemd/system/edgeboxctl.service
sudo systemctl daemon-reload
@echo "Edgeboxctl installed successfully"
@echo "To start edgeboxctl run: systemctl start edgeboxctl"
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:
systemctl start edgeboxctl
stop:
systemctl stop edgeboxctl
status:
systemctl status edgeboxctl
log: start
journalctl -fu edgeboxctl
+8 -49
View File
@@ -1,21 +1,3 @@
<!-- 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 --> <!-- PROJECT LOGO -->
<br /> <br />
<p align="center"> <p align="center">
@@ -26,9 +8,7 @@
<h3 align="center">Edgebox Control Module</h3> <h3 align="center">Edgebox Control Module</h3>
<p align="center"> <p align="center">
A System Control module written in Go. Its resonsability is to configure dependencies and perform system tasks, automatically in a schedule, or by command. A System Control module written in Go. Its responsibility is to configure dependencies and perform system tasks, automatically on a schedule, or on demand.
<br />
<a href="https://github.com/github_username/edgeboxctl"><strong>Explore the docs »</strong></a>
<br /> <br />
<br /> <br />
<a href="https://github.com/github_username/edgeboxctl/issues">Report Bug</a> <a href="https://github.com/github_username/edgeboxctl/issues">Report Bug</a>
@@ -58,9 +38,7 @@
<!-- ABOUT THE PROJECT --> <!-- ABOUT THE PROJECT -->
## About The Project ## About The Project
[![Edgebox Screen Shot][product-screenshot]](https://edgebox.co) 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 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 ### Built With
@@ -78,18 +56,15 @@ To get a local copy up and running follow these simple steps.
### Prerequisites ### Prerequisites
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. If you're running for development purposes, a docker container suffices, so make sure you have:
If you're running for development purposes, a Docker container suffices, so make sure you have: * docker
* Docker * docker compose
* 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/). Check the following links for more info on [Docker](https://www.docker.com/) and [Docker Compose](https://docs.docker.com/compose/).
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*`) * `arm-linux-gnueabi-gcc` (`sudo apt-get install gcc-arm*`)
* `sh` * `sh`
* `rm` * `rm`
@@ -141,20 +116,4 @@ Contributions are what make the open source community such an amazing place to b
<!-- LICENSE --> <!-- LICENSE -->
## License ## License
Distributed under the MIT License. See `LICENSE` for more information. Distributed under the Elastic License 2.0. 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
+1 -1
View File
@@ -99,7 +99,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) // 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 { func isSystemReady() bool {
_, err := os.Stat("/home/system/components/ws/.ready") _, err := os.Stat(utils.GetPath(utils.WsPath) + ".ready")
return !os.IsNotExist(err) return !os.IsNotExist(err)
} }
+29
View File
@@ -0,0 +1,29 @@
[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
+28
View File
@@ -0,0 +1,28 @@
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"`
}
+316 -46
View File
@@ -9,7 +9,9 @@ import (
"github.com/joho/godotenv" "github.com/joho/godotenv"
"github.com/edgebox-iot/edgeboxctl/internal/system"
"github.com/edgebox-iot/edgeboxctl/internal/utils" "github.com/edgebox-iot/edgeboxctl/internal/utils"
"github.com/edgebox-iot/edgeboxctl/internal/diagnostics"
) )
// EdgeApp : Struct representing an EdgeApp in the system // EdgeApp : Struct representing an EdgeApp in the system
@@ -17,11 +19,15 @@ type EdgeApp struct {
ID string `json:"id"` ID string `json:"id"`
Name string `json:"name"` Name string `json:"name"`
Description string `json:"description"` Description string `json:"description"`
Experimental bool `json:"experimental"`
Status EdgeAppStatus `json:"status"` Status EdgeAppStatus `json:"status"`
Services []EdgeAppService `json:"services"` Services []EdgeAppService `json:"services"`
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"`
Login EdgeAppLogin `json:"login"`
} }
// MaybeEdgeApp : Boolean flag for validation of edgeapp existance // MaybeEdgeApp : Boolean flag for validation of edgeapp existance
@@ -42,9 +48,30 @@ 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"`
}
type EdgeAppLogin struct {
Enabled bool `json:"enabled"`
Username string `json:"username"`
Password string `json:"password"`
}
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 authEnvFilename = "/auth.env"
const runnableFilename = "/.run" const runnableFilename = "/.run"
const appdataFoldername = "/appdata"
const postInstallFilename = "/edgebox-postinstall.done"
const myEdgeAppServiceEnvFilename = "/myedgeapp.env" const myEdgeAppServiceEnvFilename = "/myedgeapp.env"
const defaultContainerOperationSleepTime time.Duration = time.Second * 10 const defaultContainerOperationSleepTime time.Duration = time.Second * 10
@@ -56,14 +83,16 @@ func GetEdgeApp(ID string) MaybeEdgeApp {
Valid: false, Valid: false,
} }
_, err := os.Stat(utils.GetPath("edgeAppsPath") + ID + configFilename) _, err := os.Stat(utils.GetPath(utils.EdgeAppsPath) + ID + configFilename)
if !os.IsNotExist(err) { if !os.IsNotExist(err) {
// File exists. Start digging! // File exists. Start digging!
edgeAppName := ID edgeAppName := ID
edgeAppDescription := "" edgeAppDescription := ""
edgeAppExperimental := false
edgeAppOptions := []EdgeAppOption{}
edgeAppEnv, err := godotenv.Read(utils.GetPath("edgeAppsPath") + ID + envFilename) edgeAppEnv, err := godotenv.Read(utils.GetPath(utils.EdgeAppsPath) + ID + envFilename)
if err != nil { if err != nil {
log.Println("Error loading .env file for edgeapp " + edgeAppName) log.Println("Error loading .env file for edgeapp " + edgeAppName)
@@ -74,12 +103,112 @@ func GetEdgeApp(ID string) MaybeEdgeApp {
if edgeAppEnv["EDGEAPP_DESCRIPTION"] != "" { if edgeAppEnv["EDGEAPP_DESCRIPTION"] != "" {
edgeAppDescription = 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 edgeAppInternetAccessible := false
edgeAppInternetURL := "" edgeAppInternetURL := ""
myEdgeAppServiceEnv, err := godotenv.Read(utils.GetPath("edgeAppsPath") + ID + myEdgeAppServiceEnvFilename) myEdgeAppServiceEnv, err := godotenv.Read(utils.GetPath(utils.EdgeAppsPath) + ID + myEdgeAppServiceEnvFilename)
if err != nil { 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")
} else { } else {
@@ -89,16 +218,36 @@ 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{ result = MaybeEdgeApp{
EdgeApp: EdgeApp{ EdgeApp: EdgeApp{
ID: ID, ID: ID,
Name: edgeAppName, Name: edgeAppName,
Description: edgeAppDescription, Description: edgeAppDescription,
Experimental: edgeAppExperimental,
Status: GetEdgeAppStatus(ID), Status: GetEdgeAppStatus(ID),
Services: GetEdgeAppServices(ID), Services: GetEdgeAppServices(ID),
InternetAccessible: edgeAppInternetAccessible, InternetAccessible: edgeAppInternetAccessible,
NetworkURL: ID + ".edgebox.local", NetworkURL: ID + "." + system.GetHostname() + ".local",
InternetURL: edgeAppInternetURL, InternetURL: edgeAppInternetURL,
Options: edgeAppOptions,
NeedsConfig: needsConfig,
Login: EdgeAppLogin{edgeAppBasicAuthEnabled, edgeAppBasicAuthUsername, edgeAppBasicAuthPassword},
}, },
Valid: true, Valid: true,
} }
@@ -113,7 +262,7 @@ func IsEdgeAppInstalled(ID string) bool {
result := false result := false
_, err := os.Stat(utils.GetPath("edgeAppsPath") + ID + runnableFilename) _, err := os.Stat(utils.GetPath(utils.EdgeAppsPath) + ID + runnableFilename)
if !os.IsNotExist(err) { if !os.IsNotExist(err) {
result = true result = true
} }
@@ -122,20 +271,53 @@ 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 { func SetEdgeAppInstalled(ID string) bool {
result := true result := true
_, err := os.Stat(utils.GetPath("edgeAppsPath") + ID + runnableFilename) if writeAppRunnableFiles(ID) {
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() buildFrameworkContainers()
@@ -150,13 +332,63 @@ func SetEdgeAppInstalled(ID string) bool {
} }
func SetEdgeAppNotInstalled(ID string) bool { func SetEdgeAppBulkInstalled(IDs []string) bool {
result := true result := true
err := os.Remove(utils.GetPath("edgeAppsPath") + ID + runnableFilename)
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)
if err != nil { if err != nil {
result = false result = false
log.Fatal(err) 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)
} }
buildFrameworkContainers() buildFrameworkContainers()
@@ -172,7 +404,7 @@ func GetEdgeApps() []EdgeApp {
// Building list of available edgeapps in the system with their status // Building list of available edgeapps in the system with their status
files, err := ioutil.ReadDir(utils.GetPath("edgeAppsPath")) files, err := ioutil.ReadDir(utils.GetPath(utils.EdgeAppsPath))
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
@@ -237,20 +469,32 @@ func GetEdgeAppStatus(ID string) EdgeAppStatus {
// GetEdgeAppServices : Returns a // GetEdgeAppServices : Returns a
func GetEdgeAppServices(ID string) []EdgeAppService { func GetEdgeAppServices(ID string) []EdgeAppService {
wsPath := utils.GetPath(utils.WsPath)
cmdArgs := []string{"-r", ".services | keys[]", utils.GetPath("edgeAppsPath") + ID + configFilename} cmdArgs := []string{"-r", ".services | keys[]", utils.GetPath(utils.EdgeAppsPath) + ID + configFilename}
servicesString := utils.Exec(utils.GetPath("wsPath"), "yq", cmdArgs) servicesString := utils.Exec(utils.GetPath(utils.WsPath), "yq", cmdArgs)
serviceSlices := strings.Split(servicesString, "\n") serviceSlices := strings.Split(servicesString, "\n")
serviceSlices = utils.DeleteEmptySlices(serviceSlices) serviceSlices = utils.DeleteEmptySlices(serviceSlices)
var edgeAppServices []EdgeAppService var edgeAppServices []EdgeAppService
for _, serviceID := range serviceSlices { for _, serviceID := range serviceSlices {
cmdArgs = []string{"-f", utils.GetPath("wsPath") + "/docker-compose.yml", "exec", "-T", serviceID, "echo", "'Service Check'"} shouldBeRunning := false
cmdResult := utils.Exec(utils.GetPath("wsPath"), "docker-compose", cmdArgs)
isRunning := false isRunning := false
if cmdResult != "" {
isRunning = true // 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
} }
// 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-compose", cmdArgs)
if cmdResult != "" {
isRunning = true
}
}
edgeAppServices = append(edgeAppServices, EdgeAppService{ID: serviceID, IsRunning: isRunning}) edgeAppServices = append(edgeAppServices, EdgeAppService{ID: serviceID, IsRunning: isRunning})
} }
@@ -260,16 +504,14 @@ func GetEdgeAppServices(ID string) []EdgeAppService {
// RunEdgeApp : Run an EdgeApp and return its most current status // RunEdgeApp : Run an EdgeApp and return its most current status
func RunEdgeApp(ID string) EdgeAppStatus { func RunEdgeApp(ID string) EdgeAppStatus {
wsPath := utils.GetPath(utils.WsPath)
services := GetEdgeAppServices(ID) services := GetEdgeAppServices(ID)
cmdArgs := []string{} cmdArgs := []string{}
for _, service := range services { for _, service := range services {
cmdArgs = []string{"-f", utils.GetPath("wsPath") + "/docker-compose.yml", "start", service.ID} cmdArgs = []string{"-f", wsPath + "/docker-compose.yml", "start", service.ID}
utils.Exec(utils.GetPath("wsPath"), "docker-compose", cmdArgs) utils.Exec(wsPath, "docker-compose", cmdArgs)
} }
// Wait for it to settle up before continuing... // Wait for it to settle up before continuing...
@@ -281,16 +523,13 @@ func RunEdgeApp(ID string) EdgeAppStatus {
// StopEdgeApp : Stops an EdgeApp and return its most current status // StopEdgeApp : Stops an EdgeApp and return its most current status
func StopEdgeApp(ID string) EdgeAppStatus { func StopEdgeApp(ID string) EdgeAppStatus {
wsPath := utils.GetPath(utils.WsPath)
services := GetEdgeAppServices(ID) services := GetEdgeAppServices(ID)
cmdArgs := []string{} cmdArgs := []string{}
for _, service := range services { for _, service := range services {
cmdArgs = []string{"-f", utils.GetPath("wsPath") + "/docker-compose.yml", "stop", service.ID} cmdArgs = []string{"-f", wsPath + "/docker-compose.yml", "stop", service.ID}
utils.Exec(utils.GetPath("wsPath"), "docker-compose", cmdArgs) utils.Exec(wsPath, "docker-compose", cmdArgs)
} }
// Wait for it to settle up before continuing... // Wait for it to settle up before continuing...
@@ -300,13 +539,43 @@ 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) // 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 { func EnableOnline(ID string, InternetURL string) MaybeEdgeApp {
maybeEdgeApp := GetEdgeApp(ID) maybeEdgeApp := GetEdgeApp(ID)
if maybeEdgeApp.Valid { // We're only going to do this operation if the EdgeApp actually exists. 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 // Create the myedgeapp.env file and add the InternetURL entry to it
envFilePath := utils.GetPath("edgeAppsPath") + ID + myEdgeAppServiceEnvFilename envFilePath := utils.GetPath(utils.EdgeAppsPath) + ID + myEdgeAppServiceEnvFilename
env, _ := godotenv.Unmarshal("INTERNET_URL=" + InternetURL) env, _ := godotenv.Unmarshal("INTERNET_URL=" + InternetURL)
_ = godotenv.Write(env, envFilePath) _ = godotenv.Write(env, envFilePath)
} }
@@ -320,13 +589,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). // DisableOnline : Removes env files necessary for system external access config. Rebuilds containers in project (in case of change only).
func DisableOnline(ID string) MaybeEdgeApp { func DisableOnline(ID string) MaybeEdgeApp {
envFilePath := utils.GetPath("edgeAppsPath") + ID + myEdgeAppServiceEnvFilename envFilePath := utils.GetPath(utils.EdgeAppsPath) + ID + myEdgeAppServiceEnvFilename
_, err := godotenv.Read(envFilePath) _, err := godotenv.Read(envFilePath)
if err != nil { if err != nil {
log.Println("myedge.app environment file for " + ID + " not found. No need to delete.") log.Println("myedge.app environment file for " + ID + " not found. No need to delete.")
} else { } else {
cmdArgs := []string{envFilePath} cmdArgs := []string{envFilePath}
utils.Exec(utils.GetPath("wsPath"), "rm", cmdArgs) utils.Exec(utils.GetPath(utils.WsPath), "rm", cmdArgs)
} }
buildFrameworkContainers() buildFrameworkContainers()
@@ -337,7 +606,7 @@ func DisableOnline(ID string) MaybeEdgeApp {
func EnablePublicDashboard(InternetURL string) bool { func EnablePublicDashboard(InternetURL string) bool {
envFilePath := utils.GetPath("apiPath") + myEdgeAppServiceEnvFilename envFilePath := utils.GetPath(utils.ApiPath) + myEdgeAppServiceEnvFilename
env, _ := godotenv.Unmarshal("INTERNET_URL=" + InternetURL) env, _ := godotenv.Unmarshal("INTERNET_URL=" + InternetURL)
_ = godotenv.Write(env, envFilePath) _ = godotenv.Write(env, envFilePath)
@@ -348,28 +617,29 @@ func EnablePublicDashboard(InternetURL string) bool {
} }
func DisablePublicDashboard() bool { func DisablePublicDashboard() bool {
envFilePath := utils.GetPath("apiPath") + myEdgeAppServiceEnvFilename envFilePath := utils.GetPath(utils.ApiPath) + myEdgeAppServiceEnvFilename
if !IsPublicDashboard() { if !IsPublicDashboard() {
log.Println("myedge.app environment file for the dashboard / api not found. No need to delete.") log.Println("myedge.app environment file for the dashboard / api not found. No need to delete.")
return false return false
} }
cmdArgs := []string{envFilePath} cmdArgs := []string{envFilePath}
utils.Exec(utils.GetPath("apiPath"), "rm", cmdArgs) utils.Exec(utils.GetPath(utils.ApiPath), "rm", cmdArgs)
buildFrameworkContainers() buildFrameworkContainers()
return true return true
} }
func IsPublicDashboard() bool { func IsPublicDashboard() bool {
envFilePath := utils.GetPath("apiPath") + myEdgeAppServiceEnvFilename envFilePath := utils.GetPath(utils.ApiPath) + myEdgeAppServiceEnvFilename
_, err := godotenv.Read(envFilePath) _, err := godotenv.Read(envFilePath)
return err == nil return err == nil
} }
func buildFrameworkContainers() { func buildFrameworkContainers() {
cmdArgs := []string{utils.GetPath("wsPath") + "ws", "--build"} wsPath := utils.GetPath(utils.WsPath)
utils.ExecAndStream(utils.GetPath("wsPath"), "sh", cmdArgs) cmdArgs := []string{wsPath + "ws", "--build"}
utils.ExecAndStream(wsPath, "sh", cmdArgs)
time.Sleep(defaultContainerOperationSleepTime) time.Sleep(defaultContainerOperationSleepTime)
+27 -5
View File
@@ -2,6 +2,7 @@ package storage
import ( import (
"fmt" "fmt"
"strconv"
"os" "os"
"path/filepath" "path/filepath"
"strings" "strings"
@@ -73,12 +74,13 @@ const (
DISK_TYPE_SDA DeviceIdentifier = "sda" DISK_TYPE_SDA DeviceIdentifier = "sda"
DISK_TYPE_MCBLK DeviceIdentifier = "mmcblk0" DISK_TYPE_MCBLK DeviceIdentifier = "mmcblk0"
DISK_TYPE_VDA DeviceIdentifier = "vda" DISK_TYPE_VDA DeviceIdentifier = "vda"
MIN_DISK_SIZE int = 1048576 // 1GB in bytes
) )
func GetDeviceIdentifier(release_version diagnostics.ReleaseVersion) DeviceIdentifier { func GetDeviceIdentifier(release_version diagnostics.ReleaseVersion) DeviceIdentifier {
switch release_version { switch release_version {
case diagnostics.CLOUD_VERSION: case diagnostics.CLOUD_VERSION:
return DISK_TYPE_VDA return DISK_TYPE_SDA
case diagnostics.PROD_VERSION: case diagnostics.PROD_VERSION:
return DISK_TYPE_MCBLK return DISK_TYPE_MCBLK
} }
@@ -134,7 +136,13 @@ func GetDevices(release_version diagnostics.ReleaseVersion) []Device {
currentDevice.InUse = currentDeviceInUseFlag currentDevice.InUse = currentDeviceInUseFlag
currentDeviceInUseFlag = false currentDeviceInUseFlag = false
currentPartitions = []Partition{} currentPartitions = []Partition{}
devices = append(devices, currentDevice) size, err := strconv.Atoi(currentDevice.Size)
if err != nil {
size = 0
}
if size > MIN_DISK_SIZE {
devices = append(devices, currentDevice)
}
} else { } else {
firstDevice = false firstDevice = false
} }
@@ -193,8 +201,22 @@ func GetDevices(release_version diagnostics.ReleaseVersion) []Device {
currentDevice.Status.Description = "Not configured" currentDevice.Status.Description = "Not configured"
} }
currentDevice.InUse = currentDeviceInUseFlag 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) devices = getDevicesSpaceUsage(devices)
return devices return devices
@@ -224,9 +246,9 @@ func getDevicesSpaceUsage(devices []Device) []Device {
bucketsUsageSplit := (uint64)(0) bucketsUsageSplit := (uint64)(0)
othersUsageSplit := (uint64)(0) othersUsageSplit := (uint64)(0)
edgeappsDirSize, _ := getDirSize(utils.GetPath("edgeAppsPath")) edgeappsDirSize, _ := getDirSize(utils.GetPath(utils.EdgeAppsPath))
// TODO for later: Figure out to get correct paths for each partition... // TODO for later: Figure out to get correct paths for each partition...
wsAppDataDirSize, _ := getDirSize("/home/system/components/ws/appdata") wsAppDataDirSize, _ := getDirSize(utils.GetPath(utils.WsPath) + "/appdata")
if partition.Mountpoint == "/" { if partition.Mountpoint == "/" {
edgeappsUsageSplit = edgeappsDirSize + wsAppDataDirSize edgeappsUsageSplit = edgeappsDirSize + wsAppDataDirSize
+435 -5
View File
@@ -4,14 +4,27 @@ import (
"fmt" "fmt"
"strconv" "strconv"
"strings" "strings"
"log"
"os"
"io"
"os/exec"
"bufio"
"path/filepath"
"io/ioutil"
"encoding/json"
"github.com/edgebox-iot/edgeboxctl/internal/utils" "github.com/edgebox-iot/edgeboxctl/internal/utils"
"github.com/shirou/gopsutil/host"
"github.com/joho/godotenv" "github.com/joho/godotenv"
"github.com/shirou/gopsutil/host"
) )
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 // GetUptimeInSeconds: Returns a value (as string) of the total system uptime
func GetUptimeInSeconds() string { func GetUptimeInSeconds() string {
uptime, _ := host.Uptime() uptime, _ := host.Uptime()
@@ -64,7 +77,8 @@ func GetHostname() string {
func SetupCloudOptions() { func SetupCloudOptions() {
var cloudEnv map[string]string var cloudEnv map[string]string
cloudEnv, err := godotenv.Read(utils.GetPath("cloudEnvFileLocation")) cloudEnvFileLocationPath := utils.GetPath(utils.CloudEnvFileLocation)
cloudEnv, err := godotenv.Read(cloudEnvFileLocationPath)
if err != nil { if err != nil {
fmt.Println("Error loading .env file for cloud version setup") fmt.Println("Error loading .env file for cloud version setup")
@@ -78,11 +92,427 @@ func SetupCloudOptions() {
utils.WriteOption("EMAIL", cloudEnv["EMAIL"]) 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"] != "" { if cloudEnv["EDGEBOXIO_API_TOKEN"] != "" {
utils.WriteOption("EDGEBOXIO_API_TOKEN", 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. // 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{utils.GetPath("cloudEnvFileLocation")}) 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
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")
}
+968 -14
View File
File diff suppressed because it is too large Load Diff
+90 -10
View File
@@ -16,7 +16,7 @@ import (
) )
// ExecAndStream : Runs a terminal command, but streams progress instead of outputting. Ideal for long lived process that need to be logged. // 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) { func ExecAndStream(path string, command string, args []string) string {
cmd := exec.Command(command, args...) cmd := exec.Command(command, args...)
@@ -27,13 +27,17 @@ func ExecAndStream(path string, command string, args []string) {
err := cmd.Run() err := cmd.Run()
outStr, errStr := string(stdoutBuf.Bytes()), string(stderrBuf.Bytes())
returnVal := outStr
if err != nil { if err != nil {
fmt.Printf("cmd.Run() failed with %s\n", err) 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) 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. // Exec : Runs a terminal Command, catches and logs errors, returns the result.
@@ -81,7 +85,7 @@ func DeleteEmptySlices(s []string) []string {
func GetSQLiteDbConnectionDetails() string { func GetSQLiteDbConnectionDetails() string {
var apiEnv map[string]string var apiEnv map[string]string
apiEnv, err := godotenv.Read(GetPath("apiEnvFileLocation")) apiEnv, err := godotenv.Read(GetPath(ApiEnvFileLocation))
if err != nil { if err != nil {
log.Fatal("Error loading .env file") log.Fatal("Error loading .env file")
@@ -100,6 +104,16 @@ func GetSQLiteFormattedDateTime(t time.Time) string {
return formatedDatetime 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 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 {
@@ -113,15 +127,15 @@ func GetPath(pathKey string) string {
} }
switch pathKey { switch pathKey {
case "cloudEnvFileLocation": case CloudEnvFileLocation:
if env["CLOUD_ENV_FILE_LOCATION"] != "" { if env["CLOUD_ENV_FILE_LOCATION"] != "" {
targetPath = env["CLOUD_ENV_FILE_LOCATION"] targetPath = env["CLOUD_ENV_FILE_LOCATION"]
} else { } else {
targetPath = "/home/system/components/edgeboxctl/cloud.env" targetPath = "/home/system/components/api/cloud.env"
} }
case "apiEnvFileLocation": case ApiEnvFileLocation:
if env["API_ENV_FILE_LOCATION"] != "" { if env["API_ENV_FILE_LOCATION"] != "" {
targetPath = env["API_ENV_FILE_LOCATION"] targetPath = env["API_ENV_FILE_LOCATION"]
@@ -129,15 +143,15 @@ func GetPath(pathKey string) string {
targetPath = "/home/system/components/api/edgebox.env" targetPath = "/home/system/components/api/edgebox.env"
} }
case "apiPath": case ApiPath:
if env["API_PATH"] != "" { if env["API_PATH"] != "" {
targetPath = env["APT_PATH"] targetPath = env["API_PATH"]
} else { } else {
targetPath = "/home/system/components/api/" targetPath = "/home/system/components/api/"
} }
case "edgeAppsPath": case EdgeAppsPath:
if env["EDGEAPPS_PATH"] != "" { if env["EDGEAPPS_PATH"] != "" {
targetPath = env["EDGEAPPS_PATH"] targetPath = env["EDGEAPPS_PATH"]
@@ -145,7 +159,14 @@ func GetPath(pathKey string) string {
targetPath = "/home/system/components/apps/" targetPath = "/home/system/components/apps/"
} }
case "wsPath": case EdgeAppsBackupPath:
if env["EDGEAPPS_BACKUP_PATH"] != "" {
targetPath = env["EDGEAPPS_BACKUP_PATH"]
} else {
targetPath = "/home/system/components/backups/"
}
case WsPath:
if env["WS_PATH"] != "" { if env["WS_PATH"] != "" {
targetPath = env["WS_PATH"] targetPath = env["WS_PATH"]
@@ -153,6 +174,21 @@ 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:
if env["BACKUP_PASSWORD_FILE_LOCATION"] != "" {
targetPath = env["BACKUP_PASSWORD_FILE_LOCATION"]
} else {
targetPath = "/home/system/components/backups/pw.txt"
}
default: default:
log.Printf("path_key %s nonexistant in GetPath().\n", pathKey) log.Printf("path_key %s nonexistant in GetPath().\n", pathKey)
@@ -186,3 +222,47 @@ func WriteOption(optionKey string, optionValue string) {
db.Close() 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
}
// 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()
}
+1 -2
View File
@@ -89,8 +89,7 @@ func TestGetPath(t *testing.T) {
t.Fail() t.Fail()
} }
validPathKey := "wsPath" result = GetPath(WsPath)
result = GetPath(validPathKey)
if result != "/home/system/components/ws/" { if result != "/home/system/components/ws/" {
t.Log("Expected /home/system/components/ws/ but got", result) t.Log("Expected /home/system/components/ws/ but got", result)
t.Fail() t.Fail()
+6
View File
@@ -0,0 +1,6 @@
#!/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
@@ -0,0 +1,7 @@
#!/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
@@ -0,0 +1,7 @@
#!/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