Author SHA1 Message Date
Paulo Truta 9339e6cf09 Storage Module Basis (#13)
* Added storage module, base structs, getDevices() func, taskGetStorageDevices (ticks every 30)

* GetDevices now returns a complete description w/ disk partitions

* Added InUse flag to Device, fixed logic bugs

* Added disk space usage for partitions in use by the system

* Cleanup

* Added UsageStat and UsageSplit filling for in use devices

* Changed value of lsblk block usage output to bytes

* Cleanup, small fixes

* Added ExecAndGetLines util func

* Added explicit detection of disk or part block type, ignoring non-suppoorted devices

* Added command line tool dependencies for the project in README file

* Added comment to func ExecAndGetLines

* Removed GetMySQLDbConnectionDetails()

* Added tests to utils module

* Fix invalid argument type for Exec family of tests

* Removed too much verbosity from utils.GetPath when no env file is available, added new argument (path) to Exec family of funcs

* Added new path argument to utils.Exec... test

* Fixed TestExec

* Added spaces trim to Exec result

* Trimming spaces and newlines from result of Exec

* Better visibility for test result log of TestExec

* Added ExampleExecAndStream test

* Added Example tests with failure status for ExecAndStream

* Added branch to test for valid key in GetPath

* Added missing t.Fail() statements in utils_test.go

* Added storage_test.go file, smoke test

* TestGetDevices
2021-06-13 12:03:36 +02:00
Malachi Soord 92ac9c2b03 Add coverage (#15)
* Add coverage

* Add codecov.yml
2021-06-08 22:40:53 +02:00
Malachi Soord e611bf2ac4 Add test foundations (#14) 2021-06-08 14:13:22 +02:00
Paulo Truta c9f97d42bf Merge pull request #10 from edgebox-iot/fix-result-null-conversion
Fix query panic on GetNextTask()
2021-06-02 13:11:48 +02:00
Paulo Truta 1736b04a66 Query contains named columns 2021-06-01 23:53:17 +00:00
Paulo Truta 5d69a9fb22 Merge pull request #9 from edgebox-iot/edgeapp_description_support
Added support for adding EDGEAPP_DESCRIPTION env var to edgebox.env
2021-06-01 16:51:48 +02:00
Paulo Truta 3ef90ffb15 Added support for adding EDGEAPP_DESCRIPTION env var to edgebox.env edgeapps 2021-06-01 11:40:58 +00:00
Paulo Truta 1ad554b9bf Merge pull request #8 from edgebox-iot/sqlite
Supporting connection to SQLite database
2021-06-01 13:28:52 +02:00
Paulo Truta df3f8fe904 Added system module, GetUptimeSeconds and GetUptimeFormatted funcs, taskGetSystemUptime 2021-05-31 11:21:14 +00:00
Paulo Truta 770e814b6e Fixed Datetime column values. Added utils.GetSQLiteFormattedDateTime def 2021-05-30 11:28:42 +00:00
Paulo Truta 81b545f868 Fix wrong handling of task arguments in systemIterator 2021-05-22 13:30:23 +00:00
Paulo Truta 9f63ab7f03 Support for nullable Task args 2021-05-19 10:43:47 +00:00
Paulo Truta 58b7425fc7 Fixed incorrect query prepare statement, small fixes in queries 2021-05-08 20:46:36 +00:00
Paulo Truta f238b3fa46 Write queries using prepared statements, better error handling 2021-05-08 20:42:07 +00:00
Paulo Truta d975710445 Added initial debug message of SQLite DB location 2021-05-03 21:02:09 +00:00
Paulo Truta 6cb3e198f0 Supporting connection to SQLite database 2021-05-02 08:52:32 +00:00
Paulo Truta 84d7fd6a82 Merge pull request #6 from edgebox-iot/include-sum
Add Sum + Tidy CI
2021-03-05 02:21:42 +01:00
Malachi Soord e6c07ddeae Tidy names 2021-03-04 21:39:37 +01:00
Malachi Soord 217309f10d Add sum 2021-03-04 21:38:02 +01:00
Paulo Truta 74cbff1966 Stopping edgeapp before removing 2021-03-04 15:53:49 +00:00
Paulo Truta 8f14a7cd15 Fix for incorrect base assignment 2021-03-04 15:19:31 +00:00
Paulo Truta 11e9543a87 Added install / remove edgeapp tasks 2021-03-04 15:06:54 +00:00
Paulo Truta c42d96bf34 Merge pull request #5 from edgebox-iot/edgeboxctl-rename
Renaming everything to edgeboxctl
2021-03-04 15:21:19 +01:00
Paulo Truta cb3ecec5f4 Fix to makefile build-prod 2021-03-04 14:06:48 +00:00
Paulo Truta 812cf31dc4 More changes. Fix build 2021-03-04 12:48:25 +00:00
Paulo Truta 0ed5297f6d Renaming everything to edgeboxctl 2021-03-04 12:43:49 +00:00
Paulo Truta 2e92ff53fe Merge pull request #4 from edgebox-iot/loop_loop_execution
Sysctl Loop execution
2021-03-04 13:27:38 +01:00
16 changed files with 903 additions and 125 deletions
+7 -15
View File
@@ -12,25 +12,17 @@ jobs:
name: Build
runs-on: ubuntu-latest
steps:
- name: Set up Go 1.x
- name: Set up Go
uses: actions/setup-go@v2
with:
go-version: ^1.15
- name: Check out code into the Go module directory
- name: Check out code
uses: actions/checkout@v2
- name: Get dependencies
run: |
go get -v -t -d ./...
if [ -f Gopkg.toml ]; then
curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh
dep ensure
fi
- name: Build
run: make build
- name: Test
run: go test -v ./...
run: make test-with-coverage
- uses: codecov/codecov-action@v1
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: coverage.out
-3
View File
@@ -21,6 +21,3 @@ main
# Build
/bin
# Go Sum files
*.sum
+1 -1
View File
@@ -8,4 +8,4 @@ RUN go mod download
RUN go get github.com/githubnemo/CompileDaemon
ENTRYPOINT CompileDaemon --build="make build" --command=./bin/sysctl
ENTRYPOINT CompileDaemon --build="make build" --command=./bin/edgeboxctl
+12 -3
View File
@@ -1,4 +1,4 @@
PROJECT?=github.com/edgebox-iot/sysctl
PROJECT?=github.com/edgebox-iot/edgeboxctl
RELEASE ?= dev
COMMIT := $(shell git rev-parse --short HEAD)
@@ -9,15 +9,24 @@ build-all:
GOOS=linux GOARCH=amd64 make build
GOOS=linux GOARCH=arm make build
build-prod:
GOOS=linux GOARCH=arm RELEASE=prod make build
build:
@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/sysctl-${GOOS}-${GOARCH} ${PROJECT}/cmd/sysctl
cp ./bin/sysctl-${GOOS}-${GOARCH} ./bin/sysctl
-o bin/edgeboxctl-${GOOS}-${GOARCH} ${PROJECT}/cmd/edgeboxctl
cp ./bin/edgeboxctl-${GOOS}-${GOARCH} ./bin/edgeboxctl
clean:
rm -rf ${BUILD_DIR}
go clean
test:
go test -tags=unit -timeout=600s -v ./...
test-with-coverage:
go test -tags=unit -timeout=600s -v ./... -coverprofile=coverage.out
+34 -25
View File
@@ -23,17 +23,17 @@
<img src="docs/img/logo.png" alt="Logo" width="80" height="80">
</a>
<h3 align="center">Edgebox SysCtl Module</h3>
<h3 align="center">Edgebox Control Module</h3>
<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.
<br />
<a href="https://github.com/github_username/sysctl"><strong>Explore the docs »</strong></a>
<a href="https://github.com/github_username/edgeboxctl"><strong>Explore the docs »</strong></a>
<br />
<br />
<a href="https://github.com/github_username/sysctl/issues">Report Bug</a>
<a href="https://github.com/github_username/edgeboxctl/issues">Report Bug</a>
·
<a href="https://github.com/github_username/sysctl/issues">Request Feature</a>
<a href="https://github.com/github_username/edgeboxctl/issues">Request Feature</a>
</p>
</p>
@@ -42,15 +42,16 @@
<!-- TABLE OF CONTENTS -->
## Table of Contents
* [About the Project](#about-the-project)
* [Built With](#built-with)
* [Getting Started](#getting-started)
* [Prerequisites](#prerequisites)
* [Installation](#installation)
* [Usage](#usage)
* [Roadmap](#roadmap)
* [Contributing](#contributing)
* [License](#license)
- [Table of Contents](#table-of-contents)
- [About The Project](#about-the-project)
- [Built With](#built-with)
- [Getting Started](#getting-started)
- [Prerequisites](#prerequisites)
- [Installation](#installation)
- [Usage](#usage)
- [Roadmap](#roadmap)
- [Contributing](#contributing)
- [License](#license)
@@ -88,11 +89,19 @@ 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/).
Aditionally, edgeboxctl needs the following bash commands available wherever it runs:
* `sh`
* `rm`
* `systemctl`
* `lsblk`
* `yq`
* `tinc-boot` _(not mandatory)_
### Installation
1. Clone the repo
```sh
git clone https://github.com/edgebox-iot/sysctl.git
git clone https://github.com/edgebox-iot/edgeboxctl.git
```
2. Run Docker-Compose
```sh
@@ -111,7 +120,7 @@ _For more examples, please refer to the [Documentation](https://github.com/edgeb
<!-- ROADMAP -->
## Roadmap
See the [open issues](https://github.com/edgebox-iot/sysctl/issues) for a list of proposed features (and known issues).
See the [open issues](https://github.com/edgebox-iot/edgeboxctl/issues) for a list of proposed features (and known issues).
@@ -136,15 +145,15 @@ 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/sysctl.svg?style=flat-square
[contributors-url]: https://github.com/edgebox-iot/sysctl/graphs/contributors
[forks-shield]: https://img.shields.io/github/forks/edgebox-iot/sysctl.svg?style=flat-square
[forks-url]: https://github.com/edgebox-iot/sysctl/network/members
[stars-shield]: https://img.shields.io/github/stars/edgebox-iot/sysctl.svg?style=flat-square
[stars-url]: https://github.com/edgebox-iot/sysctl/stargazers
[issues-shield]: https://img.shields.io/github/issues/edgebox-iot/sysctl.svg?style=flat-square
[issues-url]: https://github.com/edgebox-iot/sysctl/issues
[license-shield]: https://img.shields.io/github/license/edgebox-iot/sysctl.svg?style=flat-square
[license-url]: https://github.com/edgebox-iot/sysctl/blob/master/LICENSE.txt
[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
+11 -8
View File
@@ -9,9 +9,9 @@ import (
"syscall"
"time"
"github.com/edgebox-iot/sysctl/internal/diagnostics"
"github.com/edgebox-iot/sysctl/internal/tasks"
"github.com/edgebox-iot/sysctl/internal/utils"
"github.com/edgebox-iot/edgeboxctl/internal/diagnostics"
"github.com/edgebox-iot/edgeboxctl/internal/tasks"
"github.com/edgebox-iot/edgeboxctl/internal/utils"
)
const defaultNotReadySleepTime time.Duration = time.Second * 60
@@ -37,7 +37,7 @@ func main() {
os.Exit(0)
}
log.Printf("Starting Sysctl service for %s", *name)
log.Printf("Starting edgeboxctl service for %s", *name)
// setup signal catching
sigs := make(chan os.Signal, 1)
@@ -74,7 +74,6 @@ func main() {
time.Sleep(defaultNotReadySleepTime)
}
}
}
@@ -93,8 +92,8 @@ func printVersion() {
func printDbDetails() {
fmt.Printf(
"\n\nDatabase Connection Information:\n %s\n\n",
utils.GetMySQLDbConnectionDetails(),
"\n\nSQLite Database Location:\n %s\n\n",
utils.GetSQLiteDbConnectionDetails(),
)
}
@@ -116,7 +115,11 @@ func systemIterator(name *string, tick int) {
tasks.ExecuteSchedules(tick)
nextTask := tasks.GetNextTask()
if nextTask.Task != "" {
log.Printf("Executing task %s / Args: %s", nextTask.Task, nextTask.Args)
taskArguments := "No arguments"
if nextTask.Args.Valid {
taskArguments = nextTask.Args.String
}
log.Printf("Executing task %s / Args: %s", nextTask.Task, taskArguments)
tasks.ExecuteTask(nextTask)
} else {
log.Printf("No tasks to execute.")
+4
View File
@@ -0,0 +1,4 @@
codecov:
require_ci_to_pass: no
comment: false
+9 -1
View File
@@ -1,9 +1,17 @@
module github.com/edgebox-iot/sysctl
module github.com/edgebox-iot/edgeboxctl
go 1.15
require (
github.com/StackExchange/wmi v0.0.0-20210224194228-fe8f1750fd46 // indirect
github.com/dariubs/percent v0.0.0-20200128140941-b7801cf1c7e2 // indirect
github.com/dustin/go-humanize v1.0.0 // indirect
github.com/go-ole/go-ole v1.2.5 // indirect
github.com/go-sql-driver/mysql v1.5.0
github.com/joho/godotenv v1.3.0
github.com/mattn/go-sqlite3 v1.14.7 // 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
gopkg.in/yaml.v2 v2.4.0
)
+72
View File
@@ -0,0 +1,72 @@
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/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/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/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=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
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-sqlite3 v1.14.7 h1:fxWBnXkxfM6sRiuH3bqJ4CfzZojMOLVc0UTsTglEghA=
github.com/mattn/go-sqlite3 v1.14.7/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
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/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/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=
github.com/tklauser/go-sysconf v0.3.6 h1:oc1sJWvKkmvIxhDHeKWvZS4f6AW+YcoguSfRF2/Hmo4=
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=
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=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/multierr v1.5.0 h1:KCa4XfM8CWFCpxXRGok+Q0SS/0XBhMDbHHGABQLvD2A=
go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
go.uber.org/zap v1.16.0 h1:uFRZXykJGK9lLY4HtgSw44DnIcAM+kRBP7x5m+NpAOM=
go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
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-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/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=
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
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.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=
+80 -7
View File
@@ -9,13 +9,14 @@ import (
"github.com/joho/godotenv"
"github.com/edgebox-iot/sysctl/internal/utils"
"github.com/edgebox-iot/edgeboxctl/internal/utils"
)
// EdgeApp : Struct representing an EdgeApp in the system
type EdgeApp struct {
ID string `json:"id"`
Name string `json:"name"`
Description string `json:"description"`
Status EdgeAppStatus `json:"status"`
Services []EdgeAppService `json:"services"`
InternetAccessible bool `json:"internet_accessible"`
@@ -43,6 +44,7 @@ type EdgeAppService struct {
const configFilename = "/edgebox-compose.yml"
const envFilename = "/edgebox.env"
const runnableFilename = "/.run"
const myEdgeAppServiceEnvFilename = "/myedgeapp.env"
const defaultContainerOperationSleepTime time.Duration = time.Second * 10
@@ -59,6 +61,7 @@ func GetEdgeApp(ID string) MaybeEdgeApp {
// File exists. Start digging!
edgeAppName := ID
edgeAppDescription := ""
edgeAppEnv, err := godotenv.Read(utils.GetPath("edgeAppsPath") + ID + envFilename)
@@ -68,6 +71,9 @@ func GetEdgeApp(ID string) MaybeEdgeApp {
if edgeAppEnv["EDGEAPP_NAME"] != "" {
edgeAppName = edgeAppEnv["EDGEAPP_NAME"]
}
if edgeAppEnv["EDGEAPP_DESCRIPTION"] != "" {
edgeAppDescription = edgeAppEnv["EDGEAPP_DESCRIPTION"]
}
}
edgeAppInternetAccessible := false
@@ -87,6 +93,7 @@ func GetEdgeApp(ID string) MaybeEdgeApp {
EdgeApp: EdgeApp{
ID: ID,
Name: edgeAppName,
Description: edgeAppDescription,
Status: GetEdgeAppStatus(ID),
Services: GetEdgeAppServices(ID),
InternetAccessible: edgeAppInternetAccessible,
@@ -102,6 +109,62 @@ func GetEdgeApp(ID string) MaybeEdgeApp {
}
func IsEdgeAppInstalled(ID string) bool {
result := false
_, err := os.Stat(utils.GetPath("edgeAppsPath") + ID + runnableFilename)
if !os.IsNotExist(err) {
result = true
}
return result
}
func SetEdgeAppInstalled(ID string) bool {
result := true
_, 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 {
// Is already installed.
result = false
}
return result
}
func SetEdgeAppNotInstalled(ID string) bool {
result := true
err := os.Remove(utils.GetPath("edgeAppsPath") + ID + runnableFilename)
if err != nil {
result = false
log.Fatal(err)
}
buildFrameworkContainers()
return result
}
// GetEdgeApps : Returns a list of all available EdgeApps in structs filled with information
func GetEdgeApps() []EdgeApp {
@@ -142,7 +205,15 @@ func GetEdgeAppStatus(ID string) EdgeAppStatus {
// - No service running = EdgeApp is off
runningServices := 0
status := EdgeAppStatus{0, "off"}
if !IsEdgeAppInstalled(ID) {
status = EdgeAppStatus{-1, "not-installed"}
} else {
services := GetEdgeAppServices(ID)
for _, edgeAppService := range services {
if edgeAppService.IsRunning {
@@ -158,6 +229,8 @@ func GetEdgeAppStatus(ID string) EdgeAppStatus {
status = EdgeAppStatus{1, "on"}
}
}
return status
}
@@ -166,14 +239,14 @@ func GetEdgeAppStatus(ID string) EdgeAppStatus {
func GetEdgeAppServices(ID string) []EdgeAppService {
cmdArgs := []string{"-r", ".services | keys[]", utils.GetPath("edgeAppsPath") + ID + configFilename}
servicesString := utils.Exec("yq", cmdArgs)
servicesString := utils.Exec(utils.GetPath("wsPath"), "yq", cmdArgs)
serviceSlices := strings.Split(servicesString, "\n")
serviceSlices = utils.DeleteEmptySlices(serviceSlices)
var edgeAppServices []EdgeAppService
for _, serviceID := range serviceSlices {
cmdArgs = []string{"-f", utils.GetPath("wsPath") + "/docker-compose.yml", "exec", "-T", serviceID, "echo", "'Service Check'"}
cmdResult := utils.Exec("docker-compose", cmdArgs)
cmdResult := utils.Exec(utils.GetPath("wsPath"), "docker-compose", cmdArgs)
isRunning := false
if cmdResult != "" {
isRunning = true
@@ -195,7 +268,7 @@ func RunEdgeApp(ID string) EdgeAppStatus {
for _, service := range services {
cmdArgs = []string{"-f", utils.GetPath("wsPath") + "/docker-compose.yml", "start", service.ID}
utils.Exec("docker-compose", cmdArgs)
utils.Exec(utils.GetPath("wsPath"), "docker-compose", cmdArgs)
}
@@ -216,7 +289,7 @@ func StopEdgeApp(ID string) EdgeAppStatus {
for _, service := range services {
cmdArgs = []string{"-f", utils.GetPath("wsPath") + "/docker-compose.yml", "stop", service.ID}
utils.Exec("docker-compose", cmdArgs)
utils.Exec(utils.GetPath("wsPath"), "docker-compose", cmdArgs)
}
@@ -253,7 +326,7 @@ func DisableOnline(ID string) MaybeEdgeApp {
log.Println("myedge.app environment file for " + ID + " not found. No need to delete.")
} else {
cmdArgs := []string{envFilePath}
utils.Exec("rm", cmdArgs)
utils.Exec(utils.GetPath("wsPath"), "rm", cmdArgs)
}
buildFrameworkContainers()
@@ -265,7 +338,7 @@ func DisableOnline(ID string) MaybeEdgeApp {
func buildFrameworkContainers() {
cmdArgs := []string{utils.GetPath("wsPath") + "ws", "--build"}
utils.ExecAndStream("sh", cmdArgs)
utils.ExecAndStream(utils.GetPath("wsPath"), "sh", cmdArgs)
time.Sleep(defaultContainerOperationSleepTime)
+272
View File
@@ -0,0 +1,272 @@
package storage
import (
"fmt"
"os"
"path/filepath"
"strings"
"github.com/edgebox-iot/edgeboxctl/internal/utils"
"github.com/shirou/gopsutil/disk"
)
// Device : Struct representing a storage device in the system
type Device struct {
ID string `json:"id"`
Name string `json:"name"`
Size string `json:"size"`
InUse bool `json:"in_use"`
MainDevice bool `json:"main_device"`
MAJ string `json:"maj"`
MIN string `json:"min"`
RM string `json:"rm"`
RO string `json:"ro"`
Partitions []Partition `json:"partitions"`
Status DeviceStatus `json:"status"`
UsageStat UsageStat `json:"usage_stat"`
}
// DeviceStatus : Struct representing possible storage device statuses (code + description)
type DeviceStatus struct {
ID int `json:"id"`
Description string `json:"description"`
}
// MaybeDevice : Boolean flag for validation of device existance
type MaybeDevice struct {
Device Device `json:"device"`
Valid bool `json:"valid"`
}
type UsageStat struct {
Total uint64 `json:"total"`
Used uint64 `json:"used"`
Free uint64 `json:"free"`
Percent string `json:"percent"`
UsageSplit UsageSplit `json:"usage_split"`
}
type UsageSplit struct {
OS uint64 `json:"os"`
EdgeApps uint64 `json:"edgeapps"`
Buckets uint64 `json:"buckets"`
Others uint64 `json:"others"`
}
// Partition : Struct representing a partition / filesystem (Empty Mountpoint means it is not mounted)
type Partition struct {
ID string `json:"id"`
Size string `json:"size"`
MAJ string `json:"maj"`
MIN string `json:"min"`
RM string `json:"rm"`
RO string `json:"ro"`
Filesystem string `json:"filesystem"`
Mountpoint string `json:"mountpoint"`
UsageStat UsageStat `json:"usage_stat"`
}
const mainDiskID = "mmcblk0"
// GetDevices : Returns a list of all available sotrage devices in structs filled with information
func GetDevices() []Device {
var devices []Device
cmdArgs := []string{"--raw", "--bytes", "--noheadings"}
scanner := utils.ExecAndGetLines("/", "lsblk", cmdArgs)
var currentDevice Device
var currentPartitions []Partition
firstDevice := true
currentDeviceInUseFlag := false
for scanner.Scan() {
// 1 Device is represented here. Extract words in order for filling a Device struct
// Example deviceRawInfo: "mmcblk0 179:0 0 29.7G 0 disk"
deviceRawInfo := strings.Fields(scanner.Text())
majMin := strings.SplitN(deviceRawInfo[1], ":", 2)
isDevice := false
isPartition := false
if deviceRawInfo[5] == "part" {
isDevice = false
isPartition = true
} else if deviceRawInfo[5] == "disk" {
isDevice = true
isPartition = false
}
if isDevice {
// Clean up on the latest device being prepared. Append all partitions found and delete the currentPartitions list afterwards.
// The first device found should not run the cleanup lines below
if !firstDevice {
currentDevice.Partitions = currentPartitions
if !currentDeviceInUseFlag {
currentDevice.Status.ID = 0
currentDevice.Status.Description = "not configured"
}
currentDevice.InUse = currentDeviceInUseFlag
currentDeviceInUseFlag = false
currentPartitions = []Partition{}
devices = append(devices, currentDevice)
} else {
firstDevice = false
}
mainDevice := false
device := Device{
ID: deviceRawInfo[0],
Name: deviceRawInfo[0],
Size: deviceRawInfo[3],
MainDevice: mainDevice,
MAJ: majMin[0],
MIN: majMin[1],
RM: deviceRawInfo[2],
RO: deviceRawInfo[4],
Status: DeviceStatus{ID: 1, Description: "healthy"},
}
if device.ID == mainDiskID {
device.MainDevice = true
}
currentDevice = device
} else if isPartition {
mountpoint := ""
if len(deviceRawInfo) >= 7 {
mountpoint = deviceRawInfo[6]
currentDeviceInUseFlag = true
}
// It is a partition, part of the last device read.
partition := Partition{
ID: deviceRawInfo[0],
Size: deviceRawInfo[3],
MAJ: majMin[0],
MIN: majMin[1],
RM: deviceRawInfo[2],
RO: deviceRawInfo[4],
Filesystem: "",
Mountpoint: mountpoint,
}
currentPartitions = append(currentPartitions, partition)
} else {
fmt.Println("Found device not compatible with Edgebox, ignoring.")
}
}
currentDevice.Partitions = currentPartitions
if !currentDeviceInUseFlag {
currentDevice.Status.ID = 0
currentDevice.Status.Description = "Not configured"
}
currentDevice.InUse = currentDeviceInUseFlag
devices = append([]Device{currentDevice}, devices...) // Prepending the first device...
devices = getDevicesSpaceUsage(devices)
return devices
}
func getDevicesSpaceUsage(devices []Device) []Device {
for deviceIndex, device := range devices {
if device.InUse {
deviceUsageStat := UsageStat{}
for partitionIndex, partition := range device.Partitions {
if partition.Mountpoint != "" {
s, _ := disk.Usage(partition.Mountpoint)
if s.Total == 0 {
continue
}
partitionUsagePercent := fmt.Sprintf("%2.f%%", s.UsedPercent)
osUsageSplit := (uint64)(0)
edgeappsUsageSplit := (uint64)(0)
bucketsUsageSplit := (uint64)(0)
othersUsageSplit := (uint64)(0)
edgeappsDirSize, _ := getDirSize(utils.GetPath("edgeAppsPath"))
// TODO for later: Figure out to get correct paths for each partition...
wsAppDataDirSize, _ := getDirSize("/home/system/components/ws/appdata")
if partition.Mountpoint == "/" {
edgeappsUsageSplit = edgeappsDirSize + wsAppDataDirSize
deviceUsageStat.UsageSplit.EdgeApps += edgeappsUsageSplit
}
if device.MainDevice {
osUsageSplit = (s.Used - othersUsageSplit - bucketsUsageSplit - edgeappsUsageSplit)
} else {
othersUsageSplit = (s.Used - bucketsUsageSplit - edgeappsUsageSplit)
}
partitionUsageSplit := UsageSplit{
OS: osUsageSplit,
EdgeApps: edgeappsUsageSplit,
Buckets: bucketsUsageSplit,
Others: othersUsageSplit,
}
deviceUsageStat.Total = deviceUsageStat.Total + s.Total
deviceUsageStat.Used = deviceUsageStat.Used + s.Used
deviceUsageStat.Free = deviceUsageStat.Free + s.Free
deviceUsageStat.UsageSplit.OS = deviceUsageStat.UsageSplit.OS + osUsageSplit
deviceUsageStat.UsageSplit.EdgeApps = deviceUsageStat.UsageSplit.EdgeApps + edgeappsUsageSplit
deviceUsageStat.UsageSplit.Buckets = deviceUsageStat.UsageSplit.Buckets + bucketsUsageSplit
deviceUsageStat.UsageSplit.Others = deviceUsageStat.UsageSplit.Others + othersUsageSplit
devices[deviceIndex].Partitions[partitionIndex].UsageStat = UsageStat{
Total: s.Total,
Used: s.Used,
Free: s.Free,
Percent: partitionUsagePercent,
UsageSplit: partitionUsageSplit,
}
}
}
devices[deviceIndex].UsageStat = deviceUsageStat
totalDevicePercentUsage := fmt.Sprintf("%2.f%%", (float32(devices[deviceIndex].UsageStat.Used)/float32(devices[deviceIndex].UsageStat.Total))*100)
devices[deviceIndex].UsageStat.Percent = totalDevicePercentUsage
}
}
return devices
}
func getDirSize(path string) (uint64, error) {
var size uint64
err := filepath.Walk(path, func(_ string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if !info.IsDir() {
size += (uint64)(info.Size())
}
return err
})
return size, err
}
+31
View File
@@ -0,0 +1,31 @@
// +build unit
package storage
import (
"testing"
)
func TestGetDevices(t *testing.T) {
result := GetDevices()
if len(result) == 0 {
t.Log("Expecting at least 1 block device, 0 elements found in slice")
t.Fail()
}
foundDevice := false
t.Log("Looking for a mmcblk0 or sda device")
for _, device := range result {
if device.ID == "mmcblk0" || device.ID == "sda" {
t.Log("Found target device", device.ID)
foundDevice = true
}
}
if !foundDevice {
t.Log("Expected to find device mmcblk0 but did not. Devices:", result)
t.Fail()
}
}
+23
View File
@@ -0,0 +1,23 @@
package system
import (
"fmt"
"strconv"
"github.com/shirou/gopsutil/host"
)
func GetUptimeInSeconds() string {
uptime, _ := host.Uptime()
return strconv.FormatUint(uptime, 10)
}
func GetUptimeFormatted() string {
uptime, _ := host.Uptime()
days := uptime / (60 * 60 * 24)
hours := (uptime - (days * 60 * 60 * 24)) / (60 * 60)
minutes := ((uptime - (days * 60 * 60 * 24)) - (hours * 60 * 60)) / 60
return fmt.Sprintf("%d days, %d hours, %d minutes", days, hours, minutes)
}
+196 -35
View File
@@ -6,18 +6,22 @@ import (
"fmt"
"log"
"strconv"
"time"
"github.com/edgebox-iot/sysctl/internal/diagnostics"
"github.com/edgebox-iot/sysctl/internal/edgeapps"
"github.com/edgebox-iot/sysctl/internal/utils"
"github.com/edgebox-iot/edgeboxctl/internal/diagnostics"
"github.com/edgebox-iot/edgeboxctl/internal/edgeapps"
"github.com/edgebox-iot/edgeboxctl/internal/storage"
"github.com/edgebox-iot/edgeboxctl/internal/system"
"github.com/edgebox-iot/edgeboxctl/internal/utils"
_ "github.com/go-sql-driver/mysql" // Mysql Driver
_ "github.com/mattn/go-sqlite3" // SQlite Driver
)
// Task : Struct for Task type
type Task struct {
ID int `json:"id"`
Task string `json:"task"`
Args string `json:"args"`
Args sql.NullString `json:"args"` // Database fields that can be null must use the sql.NullString type
Status string `json:"status"`
Result sql.NullString `json:"result"` // Database fields that can be null must use the sql.NullString type
Created string `json:"created"`
@@ -35,6 +39,14 @@ type taskStartEdgeAppArgs struct {
ID string `json:"id"`
}
type taskInstallEdgeAppArgs struct {
ID string `json:"id"`
}
type taskRemoveEdgeAppArgs struct {
ID string `json:"id"`
}
type taskStopEdgeAppArgs struct {
ID string `json:"id"`
}
@@ -52,18 +64,14 @@ type taskDisableOnlineArgs struct {
func GetNextTask() Task {
// Will try to connect to API database, which should be running locally under WS.
db, err := sql.Open("mysql", utils.GetMySQLDbConnectionDetails())
db, err := sql.Open("sqlite3", utils.GetSQLiteDbConnectionDetails())
// if there is an error opening the connection, handle it
if err != nil {
panic(err.Error())
}
// defer the close till after the main function has finished executing
defer db.Close()
// perform a db.Query insert
results, err := db.Query("SELECT * FROM tasks WHERE status = 0 ORDER BY created ASC LIMIT 1;")
results, err := db.Query("SELECT id, task, args, status, result, created, updated FROM task WHERE status = 0 ORDER BY created ASC LIMIT 1;")
// if there is an error inserting, handle it
if err != nil {
@@ -81,8 +89,8 @@ func GetNextTask() Task {
}
}
// be careful deferring Queries if you are using transactions
defer results.Close()
results.Close()
db.Close()
return task
@@ -91,18 +99,22 @@ func GetNextTask() Task {
// ExecuteTask : Performs execution of the given task, updating the task status as it goes, and publishing the task result
func ExecuteTask(task Task) Task {
db, err := sql.Open("mysql", utils.GetMySQLDbConnectionDetails())
db, err := sql.Open("sqlite3", utils.GetSQLiteDbConnectionDetails())
if err != nil {
panic(err.Error())
}
defer db.Close()
_, err = db.Query("UPDATE tasks SET status = 1 WHERE ID = " + strconv.Itoa(task.ID))
statement, err := db.Prepare("UPDATE task SET status = ?, updated = ? WHERE ID = ?;") // Prepare SQL Statement
if err != nil {
panic(err.Error())
log.Fatal(err.Error())
}
formatedDatetime := utils.GetSQLiteFormattedDateTime(time.Now())
_, err = statement.Exec(1, formatedDatetime, strconv.Itoa(task.ID)) // Execute SQL Statement
if err != nil {
log.Fatal(err.Error())
}
if diagnostics.Version == "dev" {
@@ -114,7 +126,7 @@ func ExecuteTask(task Task) Task {
log.Println("Setting up bootnode connection...")
var args taskSetupTunnelArgs
err := json.Unmarshal([]byte(task.Args), &args)
err := json.Unmarshal([]byte(task.Args.String), &args)
if err != nil {
log.Printf("Error reading arguments of setup_bootnode task: %s", err)
} else {
@@ -122,11 +134,35 @@ func ExecuteTask(task Task) Task {
task.Result = sql.NullString{String: taskResult, Valid: true}
}
case "install_edgeapp":
log.Println("Installing EdgeApp...")
var args taskInstallEdgeAppArgs
err := json.Unmarshal([]byte(task.Args.String), &args)
if err != nil {
log.Printf("Error reading arguments of install_edgeapp task: %s", err)
} else {
taskResult := taskInstallEdgeApp(args)
task.Result = sql.NullString{String: taskResult, Valid: true}
}
case "remove_edgeapp":
log.Println("Removing EdgeApp...")
var args taskRemoveEdgeAppArgs
err := json.Unmarshal([]byte(task.Args.String), &args)
if err != nil {
log.Printf("Error reading arguments of remove_edgeapp task: %s", err)
} else {
taskResult := taskRemoveEdgeApp(args)
task.Result = sql.NullString{String: taskResult, Valid: true}
}
case "start_edgeapp":
log.Println("Starting EdgeApp...")
var args taskStartEdgeAppArgs
err := json.Unmarshal([]byte(task.Args), &args)
err := json.Unmarshal([]byte(task.Args.String), &args)
if err != nil {
log.Printf("Error reading arguments of start_edgeapp task: %s", err)
} else {
@@ -138,7 +174,7 @@ func ExecuteTask(task Task) Task {
log.Println("Stopping EdgeApp...")
var args taskStopEdgeAppArgs
err := json.Unmarshal([]byte(task.Args), &args)
err := json.Unmarshal([]byte(task.Args.String), &args)
if err != nil {
log.Printf("Error reading arguments of stop_edgeapp task: %s", err)
} else {
@@ -150,7 +186,7 @@ func ExecuteTask(task Task) Task {
log.Println("Enabling online access to EdgeApp...")
var args taskEnableOnlineArgs
err := json.Unmarshal([]byte(task.Args), &args)
err := json.Unmarshal([]byte(task.Args.String), &args)
if err != nil {
log.Printf("Error reading arguments of enable_online task: %s", err)
} else {
@@ -162,7 +198,7 @@ func ExecuteTask(task Task) Task {
log.Println("Disabling online access to EdgeApp...")
var args taskDisableOnlineArgs
err := json.Unmarshal([]byte(task.Args), &args)
err := json.Unmarshal([]byte(task.Args.String), &args)
if err != nil {
log.Printf("Error reading arguments of enable_online task: %s", err)
} else {
@@ -174,16 +210,35 @@ func ExecuteTask(task Task) Task {
}
statement, err = db.Prepare("Update task SET status = ?, result = ?, updated = ? WHERE ID = ?;") // Prepare SQL Statement
if err != nil {
log.Fatal(err.Error())
}
if err != nil {
log.Fatal(err.Error())
}
formatedDatetime = utils.GetSQLiteFormattedDateTime(time.Now())
if task.Result.Valid {
db.Query("Update tasks SET status = 2, result = '" + task.Result.String + "' WHERE ID = " + strconv.Itoa(task.ID) + ";")
_, err = statement.Exec(2, task.Result.String, formatedDatetime, strconv.Itoa(task.ID)) // Execute SQL Statement with result info
if err != nil {
log.Fatal(err.Error())
}
} else {
db.Query("Update tasks SET status = 3, result = 'Error' WHERE ID = " + strconv.Itoa(task.ID) + ";")
_, err = statement.Exec(3, "Error", formatedDatetime, strconv.Itoa(task.ID)) // Execute SQL Statement with Error info
if err != nil {
log.Fatal(err.Error())
}
}
if err != nil {
panic(err.Error())
}
db.Close()
returnTask := task
return returnTask
@@ -194,8 +249,19 @@ func ExecuteTask(task Task) Task {
func ExecuteSchedules(tick int) {
if tick == 1 {
// Executing on startup (first tick). Schedules run before tasks in the SystemIterator
uptime := taskGetSystemUptime()
log.Println("Uptime is " + uptime + " seconds (" + system.GetUptimeFormatted() + ")")
log.Println(taskGetStorageDevices())
log.Println(taskGetEdgeApps())
}
if tick%5 == 0 {
// Executing every 5 ticks
log.Println(taskGetStorageDevices())
}
if tick%30 == 0 {
@@ -218,19 +284,49 @@ func taskSetupTunnel(args taskSetupTunnelArgs) string {
fmt.Println("Executing taskSetupTunnel")
cmdargs := []string{"gen", "--name", args.NodeName, "--token", args.BootnodeToken, args.BootnodeAddress + ":8655", "--prefix", args.AssignedAddress}
utils.Exec("tinc-boot", cmdargs)
utils.Exec(utils.GetPath("wsPath"), "tinc-boot", cmdargs)
cmdargs = []string{"start", "tinc@dnet"}
utils.Exec("systemctl", cmdargs)
utils.Exec(utils.GetPath("wsPath"), "systemctl", cmdargs)
cmdargs = []string{"enable", "tinc@dnet"}
utils.Exec("systemctl", cmdargs)
utils.Exec(utils.GetPath("wsPath"), "systemctl", cmdargs)
output := "OK" // Better check / logging of command execution result.
return output
}
func taskInstallEdgeApp(args taskInstallEdgeAppArgs) string {
fmt.Println("Executing taskInstallEdgeApp for " + args.ID)
result := edgeapps.SetEdgeAppInstalled(args.ID)
resultJSON, _ := json.Marshal(result)
taskGetEdgeApps()
return string(resultJSON)
}
func taskRemoveEdgeApp(args taskRemoveEdgeAppArgs) string {
fmt.Println("Executing taskRemoveEdgeApp for " + args.ID)
// Making sure the application is stopped before setting it as removed.
edgeapps.StopEdgeApp(args.ID)
result := edgeapps.SetEdgeAppNotInstalled(args.ID)
resultJSON, _ := json.Marshal(result)
taskGetEdgeApps()
return string(resultJSON)
}
func taskStartEdgeApp(args taskStartEdgeAppArgs) string {
fmt.Println("Executing taskStartEdgeApp for " + args.ID)
@@ -294,20 +390,85 @@ func taskGetEdgeApps() string {
edgeApps := edgeapps.GetEdgeApps()
edgeAppsJSON, _ := json.Marshal(edgeApps)
db, err := sql.Open("mysql", utils.GetMySQLDbConnectionDetails())
db, err := sql.Open("sqlite3", utils.GetSQLiteDbConnectionDetails())
if err != nil {
panic(err.Error())
log.Fatal(err.Error())
}
defer db.Close()
_, err = db.Query("REPLACE into options (name, value) VALUES ('EDGEAPPS_LIST','" + string(edgeAppsJSON) + "');")
statement, err := db.Prepare("REPLACE into option (name, value, created, updated) VALUES (?, ?, ?, ?);") // Prepare SQL Statement
if err != nil {
panic(err.Error())
log.Fatal(err.Error())
}
formatedDatetime := utils.GetSQLiteFormattedDateTime(time.Now())
_, err = statement.Exec("EDGEAPPS_LIST", string(edgeAppsJSON), formatedDatetime, formatedDatetime) // Execute SQL Statement
if err != nil {
log.Fatal(err.Error())
}
db.Close()
return string(edgeAppsJSON)
}
func taskGetSystemUptime() string {
fmt.Println("Executing taskGetSystemUptime")
uptime := system.GetUptimeInSeconds()
db, err := sql.Open("sqlite3", utils.GetSQLiteDbConnectionDetails())
if err != nil {
log.Fatal(err.Error())
}
statement, err := db.Prepare("REPLACE into option (name, value, created, updated) VALUES (?, ?, ?, ?);") // Prepare SQL Statement
if err != nil {
log.Fatal(err.Error())
}
formatedDatetime := utils.GetSQLiteFormattedDateTime(time.Now())
_, err = statement.Exec("SYSTEM_UPTIME", uptime, formatedDatetime, formatedDatetime) // Execute SQL Statement
if err != nil {
log.Fatal(err.Error())
}
db.Close()
return uptime
}
func taskGetStorageDevices() string {
fmt.Println("Executing taskGetStorageDevices")
devices := storage.GetDevices()
devicesJSON, _ := json.Marshal(devices)
db, err := sql.Open("sqlite3", utils.GetSQLiteDbConnectionDetails())
if err != nil {
log.Fatal(err.Error())
}
statement, err := db.Prepare("REPLACE into option (name, value, created, updated) VALUES (?, ?, ?, ?);") // Prepare SQL Statement
if err != nil {
log.Fatal(err.Error())
}
formatedDatetime := utils.GetSQLiteFormattedDateTime(time.Now())
_, err = statement.Exec("STORAGE_DEVICES_LIST", devicesJSON, formatedDatetime, formatedDatetime) // Execute SQL Statement
if err != nil {
log.Fatal(err.Error())
}
db.Close()
return string(devicesJSON)
}
+32 -16
View File
@@ -1,30 +1,33 @@
package utils
import (
"bufio"
"bytes"
"fmt"
"io"
"log"
"os"
"os/exec"
"strings"
"time"
"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(command string, args []string) {
func ExecAndStream(path string, command string, args []string) {
cmd := exec.Command(command, args...)
var stdoutBuf, stderrBuf bytes.Buffer
cmd.Stdout = io.MultiWriter(os.Stdout, &stdoutBuf)
cmd.Stderr = io.MultiWriter(os.Stderr, &stderrBuf)
cmd.Dir = GetPath("wsPath")
cmd.Dir = path
err := cmd.Run()
if err != nil {
log.Fatalf("cmd.Run() failed with %s\n", err)
fmt.Printf("cmd.Run() failed with %s\n", err)
}
outStr, errStr := string(stdoutBuf.Bytes()), string(stderrBuf.Bytes())
@@ -33,13 +36,13 @@ func ExecAndStream(command string, args []string) {
}
// Exec : Runs a terminal Command, catches and logs errors, returns the result.
func Exec(command string, args []string) string {
func Exec(path string, command string, args []string) string {
cmd := exec.Command(command, args...)
var out bytes.Buffer
var stderr bytes.Buffer
cmd.Stdout = &out
cmd.Stderr = &stderr
cmd.Dir = GetPath("wsPath")
cmd.Dir = path
err := cmd.Run()
if err != nil {
// TODO: Deal with possibility of error in command, allow explicit error handling and return proper formatted stderr
@@ -48,10 +51,20 @@ func Exec(command string, args []string) string {
// log.Println("Result: " + out.String()) // ... Silence ...
return out.String()
return strings.Trim(out.String(), " \n")
}
// Exec : Runs a terminal Command, returns the result as a *bufio.Scanner type, split in lines and ready to parse.
func ExecAndGetLines(path string, command string, args []string) *bufio.Scanner {
cmdOutput := Exec(path, command, args)
cmdOutputReader := strings.NewReader(cmdOutput)
scanner := bufio.NewScanner(cmdOutputReader)
scanner.Split(bufio.ScanLines)
return scanner
}
// DeleteEmptySlices : Given a string array, delete empty entries.
func DeleteEmptySlices(s []string) []string {
var r []string
@@ -63,8 +76,8 @@ func DeleteEmptySlices(s []string) []string {
return r
}
// GetMySQLDbConnectionDetails : Returns the necessary string as connection info for SQL.db()
func GetMySQLDbConnectionDetails() string {
// GetSQLiteDbConnectionDetails : Returns the necessary string as connection info for SQL.db()
func GetSQLiteDbConnectionDetails() string {
var apiEnv map[string]string
apiEnv, err := godotenv.Read(GetPath("apiEnvFileLocation"))
@@ -73,13 +86,17 @@ func GetMySQLDbConnectionDetails() string {
log.Fatal("Error loading .env file")
}
Dbhost := "127.0.0.1:" + apiEnv["HOST_MACHINE_MYSQL_PORT"]
Dbname := apiEnv["MYSQL_DATABASE"]
Dbuser := apiEnv["MYSQL_USER"]
Dbpass := apiEnv["MYSQL_PASSWORD"]
return apiEnv["SQLITE_DATABASE"] // Will read from api project edgebox.env file
return Dbuser + ":" + Dbpass + "@tcp(" + Dbhost + ")/" + Dbname
}
// 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 {
// This date is used to indicate the layout.
const datetimeLayout = "2006-01-02 15:04:05"
formatedDatetime := t.Format(datetimeLayout)
return formatedDatetime
}
// 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 ;)
@@ -88,11 +105,10 @@ func GetPath(pathKey string) string {
// Read whole of .env file to map.
var env map[string]string
env, err := godotenv.Read()
targetPath := ""
var targetPath string
if err != nil {
// log.Println("Project .env file not found withing project root. Using only hardcoded path variables.")
// Do Nothing...
targetPath = ""
}
switch pathKey {
+108
View File
@@ -0,0 +1,108 @@
// +build unit
package utils
import (
"testing"
"time"
)
func TestExec(t *testing.T) {
testCommand := "echo"
testArguments := []string{"Hello World"}
result := Exec("/", testCommand, testArguments)
if result != "Hello World" {
t.Log("Expected 'Hello World' but got", "'"+result+"'")
t.Fail()
}
}
func ExampleExecAndStream() {
ExecAndStream("/", "echo", []string{"Hello"})
// Output:
// Hello
//
// out:
// Hello
//
// err:
}
func ExampleExecAndStreamExecutableNotFound() {
ExecAndStream("/", "testcommand", []string{"Hello"})
// Output:
// cmd.Run() failed with exec: "testcommand": executable file not found in $PATH
//
// out:
//
// err:
}
func ExampleExecAndStreamError() {
ExecAndStream("/", "man", []string{"Hello"})
// Output:
// cmd.Run() failed with exit status 16
//
// out:
//
// err:
// No manual entry for Hello
}
func TestExecAndGetLines(t *testing.T) {
testCommand := "echo"
testArguments := []string{"$'Line1\nLine2\nLine3'"}
var result []string
scanner := ExecAndGetLines("/", testCommand, testArguments)
for scanner.Scan() {
result = append(result, scanner.Text())
}
if len(result) != 3 {
t.Log("Expected 3 lines but got ", len(result))
t.Fail()
}
}
func TestDeleteEmptySlices(t *testing.T) {
testSlice := []string{"Line1", "", "Line3"}
resultSlice := DeleteEmptySlices(testSlice)
if (len(resultSlice)) != 2 {
t.Log("Expected 2 slices but got ", len(resultSlice))
t.Fail()
}
}
func TestGetPath(t *testing.T) {
invalidPathKey := "test"
result := GetPath(invalidPathKey)
if result != "" {
t.Log("Expected empty result but got ", result)
t.Fail()
}
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) {
datetime := time.Date(2021, time.Month(1), 01, 1, 30, 15, 0, time.UTC)
result := GetSQLiteFormattedDateTime(datetime)
if result != "2021-01-01 01:30:15" {
t.Log("Expected 2021-01-01 01:30:15 but got ", result)
t.Fail()
}
}