Compare commits

...

13 Commits

Author SHA1 Message Date
Cedric Verstraeten
20d2517e74 add snapcraft 2023-07-01 07:42:12 +02:00
Cedric Verstraeten
12902e2482 disable snapcraft for the time being 2023-06-29 23:15:34 +02:00
Cedric Verstraeten
baca44beef Update docker.yml 2023-06-29 21:07:12 +02:00
Cedric Verstraeten
d7580744e2 Update docker.yml 2023-06-29 20:52:58 +02:00
Cedric Verstraeten
04f4bc9bf2 Update docker.yml 2023-06-29 20:47:56 +02:00
Cedric Verstraeten
d879174f4c add user to lxd group for snapcraft build 2023-06-29 20:40:28 +02:00
Cedric Verstraeten
5a1a62a723 Update docker.yml 2023-06-29 20:31:18 +02:00
Cedric Verstraeten
c519b01092 add snapcraft and try to snap the build 2023-06-29 20:23:11 +02:00
Cedric Verstraeten
c2ff7ff785 add missing custom config directory references 2023-06-29 20:10:16 +02:00
Cedric Verstraeten
44ec8c0534 try upgrading the dockerfile 2023-06-29 14:06:41 +02:00
Cedric Verstraeten
21c0e01137 add additional environment variables to tweak the internal agent "disable motion, disable liveview" 2023-06-29 12:28:44 +02:00
Cedric Verstraeten
f7ced6056d update to port 80 + allow frontend to take into account a custom config directory 2023-06-28 20:24:41 +02:00
Cedric Verstraeten
00917e3f88 add flag arguments instead of absolute arguments (we now support names)
added option to define the config location, can be different than the relative location of the agent binary
2023-06-28 19:28:07 +02:00
18 changed files with 172 additions and 94 deletions

View File

@@ -43,6 +43,7 @@ jobs:
run: docker buildx build --platform linux/$(echo ${{matrix.architecture}} | tr - /) -t $REPO-arch:arch-$(echo ${{matrix.architecture}} | tr / -)-${{steps.short-sha.outputs.sha}} --output type=tar,dest=output-${{matrix.architecture}}.tar .
- name: Strip binary
run: mkdir -p output/ && tar -xf output-${{matrix.architecture}}.tar -C output && rm output-${{matrix.architecture}}.tar && cd output/ && tar -cf ../agent-${{matrix.architecture}}.tar -C home/agent . && rm -rf output
# We'll make a GitHub release and push the build (tar) as an artifact
- uses: rickstaa/action-create-tag@v1
with:
tag: ${{ steps.short-sha.outputs.sha }}
@@ -54,6 +55,17 @@ jobs:
name: ${{ steps.short-sha.outputs.sha }}
tag: ${{ steps.short-sha.outputs.sha }}
artifacts: "agent-${{matrix.architecture}}.tar"
# Taken from GoReleaser's own release workflow.
# The available Snapcraft Action has some bugs described in the issue below.
# The mkdirs are a hack for https://github.com/goreleaser/goreleaser/issues/1715.
#- name: Setup Snapcraft
# run: |
# sudo apt-get update
# sudo apt-get -yq --no-install-suggests --no-install-recommends install snapcraft
# mkdir -p $HOME/.cache/snapcraft/download
# mkdir -p $HOME/.cache/snapcraft/stage-packages
#- name: Use Snapcraft
# run: tar -xf agent-${{matrix.architecture}}.tar && snapcraft
build-other:
runs-on: ubuntu-latest
permissions:

View File

@@ -10,7 +10,7 @@ ENV GOSUMDB=off
##########################################
# Installing some additional dependencies.
RUN apt-get update && apt-get install -y --no-install-recommends \
RUN apt-get upgrade -y && apt-get update && apt-get install -y --no-install-recommends \
git build-essential cmake pkg-config unzip libgtk2.0-dev \
curl ca-certificates libcurl4-openssl-dev libssl-dev libjpeg62-turbo-dev && \
rm -rf /var/lib/apt/lists/*
@@ -147,4 +147,4 @@ HEALTHCHECK CMD curl --fail http://localhost:80 || exit 1
# Leeeeettttt'ssss goooooo!!!
# Run the shizzle from the right working directory.
WORKDIR /home/agent
CMD ["./main", "run", "opensource", "80"]
CMD ["./main", "-action", "run", "-port", "80"]

View File

@@ -18,6 +18,7 @@
[![donate](https://brianmacdonald.github.io/Ethonate/svg/eth-donate-blue.svg)](https://brianmacdonald.github.io/Ethonate/address#0xf4a759C9436E2280Ea9cdd23d3144D95538fF4bE)
<a target="_blank" href="https://twitter.com/kerberosio?ref_src=twsrc%5Etfw"><img src="https://img.shields.io/twitter/url.svg?label=Follow%20%40kerberosio&style=social&url=https%3A%2F%2Ftwitter.com%2Fkerberosio" alt="Twitter Widget"></a>
[![Discord Shield](https://discordapp.com/api/guilds/1039619181731135499/widget.png?style=shield)](https://discord.gg/Bj77Vqfp2G)
[![kerberosio](https://snapcraft.io/kerberosio/badge.svg)](https://snapcraft.io/kerberosio)
[**Docker Hub**](https://hub.docker.com/r/kerberos/agent) | [**Documentation**](https://doc.kerberos.io) | [**Website**](https://kerberos.io) | [**View Demo**](https://demo.kerberos.io)
@@ -82,6 +83,16 @@ Run Kerberos Agent with [Balena Cloud](https://www.balena.io/) super powers. Mon
[![deploy with balena](https://balena.io/deploy.svg)](https://dashboard.balena-cloud.com/deploy?repoUrl=https://github.com/kerberos-io/balena-agent)
## Quickstart - Snap
Run Kerberos Agent with our [Snapcraft package](https://snapcraft.io/kerberosio).
snap install kerberosio
Once installed you can find your Kerberos Agent configration at `/var/snap/kerberosio/common`. Run the Kerberos Agent as following
kerberosio.agent -action=run -port=80
## A world of Kerberos Agents
The Kerberos Agent is an isolated and scalable video (surveillance) management agent with a strong focus on user experience, scalability, resilience, extension and integration. Next to the Kerberos Agent, Kerberos.io provides many other tools such as [Kerberos Factory](https://github.com/kerberos-io/factory), [Kerberos Vault](https://github.com/kerberos-io/vault) and [Kerberos Hub](https://github.com/kerberos-io/hub) to provide additional capabilities: bring your own cloud, bring your own storage, central overview, live streaming, machine learning etc.
@@ -120,6 +131,7 @@ We have documented the different deployment models [in the `deployments` directo
- [Terraform](https://github.com/kerberos-io/agent/tree/master/deployments#5-terraform)
- [Salt](https://github.com/kerberos-io/agent/tree/master/deployments#6-salt)
- [Balena](https://github.com/kerberos-io/agent/tree/master/deployments#8-balena)
- [Snap](https://github.com/kerberos-io/agent/tree/master/deployments#9-snap)
By default your Kerberos Agents will store all its configuration and recordings inside the container. To help you automate and have a more consistent data governance, you can attach volumes to configure and persist data of your Kerberos Agents, and/or configure each Kerberos Agent through environment variables.
@@ -183,8 +195,11 @@ Next to attaching the configuration file, it is also possible to override the co
| `AGENT_CAPTURE_IPCAMERA_ONVIF_XADDR` | ONVIF endpoint/address running on the camera. | "" |
| `AGENT_CAPTURE_IPCAMERA_ONVIF_USERNAME` | ONVIF username to authenticate against. | "" |
| `AGENT_CAPTURE_IPCAMERA_ONVIF_PASSWORD` | ONVIF password to authenticate against. | "" |
| `AGENT_CAPTURE_MOTION` | Toggle for enabling or disabling motion. | "true" |
| `AGENT_CAPTURE_LIVEVIEW` | Toggle for enabling or disabling liveview. | "true" |
| `AGENT_CAPTURE_SNAPSHOTS` | Toggle for enabling or disabling snapshot generation. | "true" |
| `AGENT_CAPTURE_RECORDING` | Toggle for enabling making recordings. | "true" |
| `AGENT_CAPTURE_CONTINUOUS` | Toggle for enabling continuous or motion based recording. | "false" |
| `AGENT_CAPTURE_CONTINUOUS` | Toggle for enabling continuous "true" or motion "false". | "false" |
| `AGENT_CAPTURE_PRERECORDING` | If `CONTINUOUS` set to `false`, specify the recording time (seconds) before after motion event. | "10" |
| `AGENT_CAPTURE_POSTRECORDING` | If `CONTINUOUS` set to `false`, specify the recording time (seconds) after motion event. | "20" |
| `AGENT_CAPTURE_MAXLENGTH` | The maximum length of a single recording (seconds). | "30" |
@@ -233,9 +248,9 @@ On opening of the GitHub Codespace, some dependencies will be installed. Once th
const dev = {
ENV: 'dev',
HOSTNAME: externalHost,
//API_URL: `${protocol}//${hostname}:8080/api`,
//URL: `${protocol}//${hostname}:8080`,
//WS_URL: `${websocketprotocol}//${hostname}:8080/ws`,
//API_URL: `${protocol}//${hostname}:80/api`,
//URL: `${protocol}//${hostname}:80`,
//WS_URL: `${websocketprotocol}//${hostname}:80/ws`,
// Uncomment, and comment the above lines, when using codespaces or other special DNS names (which you can't control)
API_URL: `${protocol}//${externalHost}/api`,
@@ -248,7 +263,7 @@ Go and open two terminals one for the `ui` project and one for the `machinery` p
1. Terminal A:
cd machinery/
go run main.go run camera 80
go run main.go -action run -port 80
2. Terminal B:
@@ -289,7 +304,7 @@ You can simply run the `machinery` using following commands.
git clone https://github.com/kerberos-io/agent
cd machinery
go run main.go run mycameraname 80
go run main.go -action run -port 80
This will launch the Kerberos Agent and run a webserver on port `80`. You can change the port by your own preference. We strongly support the usage of [Goland](https://www.jetbrains.com/go/) or [Visual Studio Code](https://code.visualstudio.com/), as it comes with all the debugging and linting features builtin.

View File

@@ -67,3 +67,13 @@ Balena Cloud provide a seamless way of building and deploying applications at sc
Together with the Balena.io team we've build a Balena App, called [`video-surveillance`](https://hub.balena.io/apps/2064752/video-surveillance), which any can use to deploy a video surveillance system in a matter of minutes with all the expected management features you can think of.
> Learn more [about Kerberos Agent with Balena](https://github.com/kerberos-io/agent/tree/master/deployments/balena).
## 9. Snap
Run Kerberos Agent with our [Snapcraft package](https://snapcraft.io/kerberosio).
snap install kerberosio
Once installed you can find your Kerberos Agent configration at `/var/snap/kerberosio/common`. Run the Kerberos Agent as following
kerberosio.agent -action=run -port=80

View File

@@ -10,7 +10,7 @@
"request": "launch",
"mode": "auto",
"program": "main.go",
"args": ["run", "cameraname", "8080"],
"args": ["-action run"],
"envFile": "${workspaceFolder}/.env",
"buildFlags": "--tags dynamic",
},

View File

@@ -2,6 +2,7 @@ package main
import (
"context"
"flag"
"os"
"time"
@@ -49,10 +50,23 @@ func main() {
}
// Start the show ;)
action := os.Args[1]
// We'll parse the flags (named variables), and start the agent.
var action string
var configDirectory string
var name string
var port string
var timeout string
flag.StringVar(&action, "action", "version", "Tell us what you want do 'run' or 'version'")
flag.StringVar(&configDirectory, "config", ".", "Where is the configuration stored")
flag.StringVar(&name, "name", "agent", "Provide a name for the agent")
flag.StringVar(&port, "port", "80", "On which port should the agent run")
flag.StringVar(&timeout, "timeout", "2000", "Number of milliseconds to wait for the ONVIF discovery to complete")
flag.Parse()
timezone, _ := time.LoadLocation("CET")
log.Log.Init(timezone)
log.Log.Init(configDirectory, timezone)
switch action {
@@ -60,14 +74,10 @@ func main() {
log.Log.Info("You are currrently running Kerberos Agent " + VERSION)
case "discover":
timeout := os.Args[2]
log.Log.Info(timeout)
case "run":
{
name := os.Args[2]
port := os.Args[3]
// Print Kerberos.io ASCII art
utils.PrintASCIIArt()
@@ -82,7 +92,7 @@ func main() {
configuration.Port = port
// Open this configuration either from Kerberos Agent or Kerberos Factory.
components.OpenConfig(&configuration)
components.OpenConfig(configDirectory, &configuration)
// We will override the configuration with the environment variables
components.OverrideWithEnvironmentVariables(&configuration)
@@ -92,18 +102,18 @@ func main() {
// Check the folder permissions, it might be that we do not have permissions to write
// recordings, update the configuration or save snapshots.
utils.CheckDataDirectoryPermissions()
utils.CheckDataDirectoryPermissions(configDirectory)
// Set timezone
timezone, _ := time.LoadLocation(configuration.Config.Timezone)
log.Log.Init(timezone)
log.Log.Init(configDirectory, timezone)
// Check if we have a device Key or not, if not
// we will generate one.
if configuration.Config.Key == "" {
key := utils.RandStringBytesMaskImpr(30)
configuration.Config.Key = key
err := components.StoreConfig(configuration.Config)
err := components.StoreConfig(configDirectory, configuration.Config)
if err == nil {
log.Log.Info("Main: updated unique key for agent to: " + key)
} else {
@@ -121,10 +131,10 @@ func main() {
CancelContext: &cancel,
HandleBootstrap: make(chan string, 1),
}
go components.Bootstrap(&configuration, &communication)
go components.Bootstrap(configDirectory, &configuration, &communication)
// Start the REST API.
routers.StartWebserver(&configuration, &communication)
routers.StartWebserver(configDirectory, &configuration, &communication)
}
default:
log.Log.Error("Main: Sorry I don't understand :(")

View File

@@ -17,7 +17,7 @@ import (
"github.com/kerberos-io/joy4/av"
)
func CleanupRecordingDirectory(configuration *models.Configuration) {
func CleanupRecordingDirectory(configDirectory string, configuration *models.Configuration) {
autoClean := configuration.Config.AutoClean
if autoClean == "true" {
maxSize := configuration.Config.MaxDirectorySize
@@ -25,7 +25,7 @@ func CleanupRecordingDirectory(configuration *models.Configuration) {
maxSize = 300
}
// Total size of the recording directory.
recordingsDirectory := "./data/recordings"
recordingsDirectory := configDirectory + "/data/recordings"
size, err := utils.DirSize(recordingsDirectory)
if err == nil {
sizeInMB := size / 1000 / 1000
@@ -51,7 +51,7 @@ func CleanupRecordingDirectory(configuration *models.Configuration) {
}
}
func HandleRecordStream(queue *pubsub.Queue, configuration *models.Configuration, communication *models.Communication, streams []av.CodecData) {
func HandleRecordStream(queue *pubsub.Queue, configDirectory string, configuration *models.Configuration, communication *models.Communication, streams []av.CodecData) {
config := configuration.Config
@@ -134,13 +134,13 @@ func HandleRecordStream(queue *pubsub.Queue, configuration *models.Configuration
}
// Create a symbol link.
fc, _ := os.Create("./data/cloud/" + name)
fc, _ := os.Create(configDirectory + "/data/cloud/" + name)
fc.Close()
recordingStatus = "idle"
// Clean up the recording directory if necessary.
CleanupRecordingDirectory(configuration)
CleanupRecordingDirectory(configDirectory, configuration)
}
// If not yet started and a keyframe, let's make a recording
@@ -192,7 +192,7 @@ func HandleRecordStream(queue *pubsub.Queue, configuration *models.Configuration
"769"
name = s + ".mp4"
fullName = "./data/recordings/" + name
fullName = configDirectory + "/data/recordings/" + name
// Running...
log.Log.Info("Recording started")
@@ -259,7 +259,7 @@ func HandleRecordStream(queue *pubsub.Queue, configuration *models.Configuration
}
// Create a symbol link.
fc, _ := os.Create("./data/cloud/" + name)
fc, _ := os.Create(configDirectory + "/data/cloud/" + name)
fc.Close()
recordingStatus = "idle"
@@ -315,7 +315,7 @@ func HandleRecordStream(queue *pubsub.Queue, configuration *models.Configuration
"769"
name := s + ".mp4"
fullName := "./data/recordings/" + name
fullName := configDirectory + "/data/recordings/" + name
// Running...
log.Log.Info("HandleRecordStream: Recording started")
@@ -406,11 +406,11 @@ func HandleRecordStream(queue *pubsub.Queue, configuration *models.Configuration
}
// Create a symbol linc.
fc, _ := os.Create("./data/cloud/" + name)
fc, _ := os.Create(configDirectory + "/data/cloud/" + name)
fc.Close()
// Clean up the recording directory if necessary.
CleanupRecordingDirectory(configuration)
CleanupRecordingDirectory(configDirectory, configuration)
}
}

View File

@@ -32,8 +32,8 @@ import (
"github.com/kerberos-io/agent/machinery/src/webrtc"
)
func PendingUpload() {
ff, err := utils.ReadDirectory("./data/cloud/")
func PendingUpload(configDirectory string) {
ff, err := utils.ReadDirectory(configDirectory + "/data/cloud/")
if err == nil {
for _, f := range ff {
log.Log.Info(f.Name())
@@ -41,12 +41,12 @@ func PendingUpload() {
}
}
func HandleUpload(configuration *models.Configuration, communication *models.Communication) {
func HandleUpload(configDirectory string, configuration *models.Configuration, communication *models.Communication) {
log.Log.Debug("HandleUpload: started")
config := configuration.Config
watchDirectory := "./data/cloud/"
watchDirectory := configDirectory + "/data/cloud/"
if config.Offline == "true" {
log.Log.Debug("HandleUpload: stopping as Offline is enabled.")
@@ -122,7 +122,7 @@ func HandleUpload(configuration *models.Configuration, communication *models.Com
// Check if we need to remove the original recording
// removeAfterUpload is set to false by default
if config.RemoveAfterUpload == "true" {
err := os.Remove("./data/recordings/" + fileName)
err := os.Remove(configDirectory + "/data/recordings/" + fileName)
if err != nil {
log.Log.Error("HandleUpload: " + err.Error())
}
@@ -614,7 +614,7 @@ func VerifyHub(c *gin.Context) {
// @Summary Will verify the persistence.
// @Description Will verify the persistence.
// @Success 200 {object} models.APIResponse
func VerifyPersistence(c *gin.Context) {
func VerifyPersistence(c *gin.Context, configDirectory string) {
var config models.Config
err := c.BindJSON(&config)
@@ -636,7 +636,7 @@ func VerifyPersistence(c *gin.Context) {
} else {
// Open test-480p.mp4
file, err := os.Open("./data/test-480p.mp4")
file, err := os.Open(configDirectory + "/data/test-480p.mp4")
if err != nil {
msg := "VerifyPersistence: error reading test-480p.mp4: " + err.Error()
log.Log.Error(msg)
@@ -741,7 +741,7 @@ func VerifyPersistence(c *gin.Context) {
"_6-967003_" + config.Name + "_200-200-400-400_24_769.mp4"
// Open test-480p.mp4
file, err := os.Open("./data/test-480p.mp4")
file, err := os.Open(configDirectory + "/test-480p.mp4")
if err != nil {
msg := "VerifyPersistence: error reading test-480p.mp4: " + err.Error()
log.Log.Error(msg)

View File

@@ -20,14 +20,14 @@ import (
"go.mongodb.org/mongo-driver/bson"
)
func GetImageFromFilePath() (image.Image, error) {
snapshotDirectory := "./data/snapshots"
func GetImageFromFilePath(configDirectory string) (image.Image, error) {
snapshotDirectory := configDirectory + "/data/snapshots"
files, err := ioutil.ReadDir(snapshotDirectory)
if err == nil && len(files) > 1 {
sort.Slice(files, func(i, j int) bool {
return files[i].ModTime().Before(files[j].ModTime())
})
filePath := "./data/snapshots/" + files[1].Name()
filePath := configDirectory + "/data/snapshots/" + files[1].Name()
f, err := os.Open(filePath)
if err != nil {
return nil, err
@@ -42,11 +42,11 @@ func GetImageFromFilePath() (image.Image, error) {
// ReadUserConfig Reads the user configuration of the Kerberos Open Source instance.
// This will return a models.User struct including the username, password,
// selected language, and if the installation was completed or not.
func ReadUserConfig() (userConfig models.User) {
func ReadUserConfig(configDirectory string) (userConfig models.User) {
for {
jsonFile, err := os.Open("./data/config/user.json")
jsonFile, err := os.Open(configDirectory + "/data/config/user.json")
if err != nil {
log.Log.Error("Config file is not found " + "./data/config/user.json, trying again in 5s: " + err.Error())
log.Log.Error("Config file is not found " + configDirectory + "/data/config/user.json, trying again in 5s: " + err.Error())
time.Sleep(5 * time.Second)
} else {
log.Log.Info("Successfully Opened user.json")
@@ -66,7 +66,7 @@ func ReadUserConfig() (userConfig models.User) {
return
}
func OpenConfig(configuration *models.Configuration) {
func OpenConfig(configDirectory string, configuration *models.Configuration) {
// We are checking which deployment this is running, so we can load
// into the configuration as expected.
@@ -146,9 +146,9 @@ func OpenConfig(configuration *models.Configuration) {
// Open device config
for {
jsonFile, err := os.Open("./data/config/config.json")
jsonFile, err := os.Open(configDirectory + "/data/config/config.json")
if err != nil {
log.Log.Error("Config file is not found " + "./data/config/config.json" + ", trying again in 5s.")
log.Log.Error("Config file is not found " + configDirectory + "/data/config/config.json" + ", trying again in 5s.")
time.Sleep(5 * time.Second)
} else {
log.Log.Info("Successfully Opened config.json from " + configuration.Name)
@@ -437,11 +437,11 @@ func OverrideWithEnvironmentVariables(configuration *models.Configuration) {
}
}
func SaveConfig(config models.Config, configuration *models.Configuration, communication *models.Communication) error {
func SaveConfig(configDirectory string, config models.Config, configuration *models.Configuration, communication *models.Communication) error {
if !communication.IsConfiguring.IsSet() {
communication.IsConfiguring.Set()
err := StoreConfig(config)
err := StoreConfig(configDirectory, config)
if err != nil {
communication.IsConfiguring.UnSet()
return err
@@ -462,7 +462,7 @@ func SaveConfig(config models.Config, configuration *models.Configuration, commu
}
}
func StoreConfig(config models.Config) error {
func StoreConfig(configDirectory string, config models.Config) error {
// Save into database
if os.Getenv("DEPLOYMENT") == "factory" || os.Getenv("MACHINERY_ENVIRONMENT") == "kubernetes" {
// Write to mongodb
@@ -484,7 +484,7 @@ func StoreConfig(config models.Config) error {
// Save into file
} else if os.Getenv("DEPLOYMENT") == "" || os.Getenv("DEPLOYMENT") == "agent" {
res, _ := json.MarshalIndent(config, "", "\t")
err := ioutil.WriteFile("./data/config/config.json", res, 0644)
err := ioutil.WriteFile(configDirectory+"/data/config/config.json", res, 0644)
return err
}

View File

@@ -23,7 +23,7 @@ import (
"github.com/tevino/abool"
)
func Bootstrap(configuration *models.Configuration, communication *models.Communication) {
func Bootstrap(configDirectory string, configuration *models.Configuration, communication *models.Communication) {
log.Log.Debug("Bootstrap: started")
// We will keep track of the Kerberos Agent up time
@@ -79,7 +79,7 @@ func Bootstrap(configuration *models.Configuration, communication *models.Commun
for {
// This will blocking until receiving a signal to be restarted, reconfigured, stopped, etc.
status := RunAgent(configuration, communication, mqttClient, uptimeStart, cameraSettings, decoder, subDecoder)
status := RunAgent(configDirectory, configuration, communication, mqttClient, uptimeStart, cameraSettings, decoder, subDecoder)
if status == "stop" {
break
@@ -87,7 +87,7 @@ func Bootstrap(configuration *models.Configuration, communication *models.Commun
if status == "not started" {
// We will re open the configuration, might have changed :O!
OpenConfig(configuration)
OpenConfig(configDirectory, configuration)
// We will override the configuration with the environment variables
OverrideWithEnvironmentVariables(configuration)
}
@@ -107,7 +107,7 @@ func Bootstrap(configuration *models.Configuration, communication *models.Commun
log.Log.Debug("Bootstrap: finished")
}
func RunAgent(configuration *models.Configuration, communication *models.Communication, mqttClient mqtt.Client, uptimeStart time.Time, cameraSettings *models.Camera, decoder *ffmpeg.VideoDecoder, subDecoder *ffmpeg.VideoDecoder) string {
func RunAgent(configDirectory string, configuration *models.Configuration, communication *models.Communication, mqttClient mqtt.Client, uptimeStart time.Time, cameraSettings *models.Camera, decoder *ffmpeg.VideoDecoder, subDecoder *ffmpeg.VideoDecoder) string {
log.Log.Debug("RunAgent: bootstrapping agent")
config := configuration.Config
@@ -260,10 +260,10 @@ func RunAgent(configuration *models.Configuration, communication *models.Communi
}
// Handle recording, will write an mp4 to disk.
go capture.HandleRecordStream(queue, configuration, communication, streams)
go capture.HandleRecordStream(queue, configDirectory, configuration, communication, streams)
// Handle Upload to cloud provider (Kerberos Hub, Kerberos Vault and others)
go cloud.HandleUpload(configuration, communication)
go cloud.HandleUpload(configDirectory, configuration, communication)
// Handle ONVIF actions
go onvif.HandleONVIFActions(configuration, communication)
@@ -284,7 +284,7 @@ func RunAgent(configuration *models.Configuration, communication *models.Communi
(*communication.CancelContext)()
// We will re open the configuration, might have changed :O!
OpenConfig(configuration)
OpenConfig(configDirectory, configuration)
// We will override the configuration with the environment variables
OverrideWithEnvironmentVariables(configuration)

View File

@@ -21,7 +21,7 @@ var Log = Logging{
var gologging = logging.MustGetLogger("gologger")
func ConfigureGoLogging(timezone *time.Location) {
func ConfigureGoLogging(configDirectory string, timezone *time.Location) {
// Logging
var format = logging.MustStringFormatter(
`%{color}%{time:15:04:05.000} %{shortfunc} ▶ %{level:.4s} %{id:03x}%{color:reset} %{message}`,
@@ -32,7 +32,7 @@ func ConfigureGoLogging(timezone *time.Location) {
stdBackend := logging.NewLogBackend(os.Stderr, "", 0)
stdBackendLeveled := logging.NewBackendFormatter(stdBackend, format)
fileBackend := logging.NewLogBackend(&lumberjack.Logger{
Filename: "./data/log/machinery.txt",
Filename: configDirectory + "/data/log/machinery.txt",
MaxSize: 2, // megabytes
Compress: true, // disabled by default
}, "", 0)
@@ -75,10 +75,10 @@ type Logging struct {
Level string
}
func (self *Logging) Init(timezone *time.Location) {
func (self *Logging) Init(configDirectory string, timezone *time.Location) {
switch self.Logger {
case "go-logging":
ConfigureGoLogging(timezone)
ConfigureGoLogging(configDirectory, timezone)
case "logrus":
ConfigureLogrus(timezone)
default:

View File

@@ -17,7 +17,7 @@ import (
"github.com/kerberos-io/agent/machinery/src/utils"
)
func AddRoutes(r *gin.Engine, authMiddleware *jwt.GinJWTMiddleware, configuration *models.Configuration, communication *models.Communication) *gin.RouterGroup {
func AddRoutes(r *gin.Engine, authMiddleware *jwt.GinJWTMiddleware, configDirectory string, configuration *models.Configuration, communication *models.Communication) *gin.RouterGroup {
r.GET("/ws", func(c *gin.Context) {
websocket.WebsocketHandler(c, communication)
@@ -40,7 +40,7 @@ func AddRoutes(r *gin.Engine, authMiddleware *jwt.GinJWTMiddleware, configuratio
var config models.Config
err := c.BindJSON(&config)
if err == nil {
err := components.SaveConfig(config, configuration, communication)
err := components.SaveConfig(configDirectory, config, configuration, communication)
if err == nil {
c.JSON(200, gin.H{
"data": "☄ Reconfiguring",
@@ -78,7 +78,7 @@ func AddRoutes(r *gin.Engine, authMiddleware *jwt.GinJWTMiddleware, configuratio
}
// The total number of recordings stored in the directory.
recordingDirectory := "./data/recordings"
recordingDirectory := configDirectory + "/data/recordings"
numberOfRecordings := utils.NumberOfMP4sInDirectory(recordingDirectory)
// All days stored in this agent.
@@ -115,7 +115,7 @@ func AddRoutes(r *gin.Engine, authMiddleware *jwt.GinJWTMiddleware, configuratio
if eventFilter.NumberOfElements == 0 {
eventFilter.NumberOfElements = 10
}
recordingDirectory := "./data/recordings"
recordingDirectory := configDirectory + "/data/recordings"
files, err := utils.ReadDirectory(recordingDirectory)
if err == nil {
events := utils.GetSortedDirectory(files)
@@ -137,7 +137,7 @@ func AddRoutes(r *gin.Engine, authMiddleware *jwt.GinJWTMiddleware, configuratio
})
api.GET("/days", func(c *gin.Context) {
recordingDirectory := "./data/recordings"
recordingDirectory := configDirectory + "/data/recordings"
files, err := utils.ReadDirectory(recordingDirectory)
if err == nil {
events := utils.GetSortedDirectory(files)
@@ -165,7 +165,7 @@ func AddRoutes(r *gin.Engine, authMiddleware *jwt.GinJWTMiddleware, configuratio
var config models.Config
err := c.BindJSON(&config)
if err == nil {
err := components.SaveConfig(config, configuration, communication)
err := components.SaveConfig(configDirectory, config, configuration, communication)
if err == nil {
c.JSON(200, gin.H{
"data": "☄ Reconfiguring",
@@ -205,7 +205,7 @@ func AddRoutes(r *gin.Engine, authMiddleware *jwt.GinJWTMiddleware, configuratio
})
api.POST("/persistence/verify", func(c *gin.Context) {
cloud.VerifyPersistence(c)
cloud.VerifyPersistence(c, configDirectory)
})
// Streaming handler
@@ -215,7 +215,7 @@ func AddRoutes(r *gin.Engine, authMiddleware *jwt.GinJWTMiddleware, configuratio
// We will only send an image once per second.
time.Sleep(time.Second * 1)
log.Log.Info("AddRoutes (/stream): reading from MJPEG stream")
img, err := components.GetImageFromFilePath()
img, err := components.GetImageFromFilePath(configDirectory)
return img, err
}
h := components.StartMotionJPEG(imageFunction, 80)

View File

@@ -35,7 +35,7 @@ import (
// @in header
// @name Authorization
func StartServer(configuration *models.Configuration, communication *models.Communication) {
func StartServer(configDirectory string, configuration *models.Configuration, communication *models.Communication) {
// Initialize REST API
r := gin.Default()
@@ -57,12 +57,12 @@ func StartServer(configuration *models.Configuration, communication *models.Comm
}
// Add all routes
AddRoutes(r, authMiddleware, configuration, communication)
AddRoutes(r, authMiddleware, configDirectory, configuration, communication)
// Update environment variables
environmentVariables := "./www/env.js"
environmentVariables := configDirectory + "/www/env.js"
if os.Getenv("AGENT_MODE") == "demo" {
demoEnvironmentVariables := "./www/env.demo.js"
demoEnvironmentVariables := configDirectory + "/www/env.demo.js"
// Move demo environment variables to environment variables
err := os.Rename(demoEnvironmentVariables, environmentVariables)
if err != nil {
@@ -71,12 +71,14 @@ func StartServer(configuration *models.Configuration, communication *models.Comm
}
// Add static routes to UI
r.Use(static.Serve("/", static.LocalFile("./www", true)))
r.Use(static.Serve("/dashboard", static.LocalFile("./www", true)))
r.Use(static.Serve("/media", static.LocalFile("./www", true)))
r.Use(static.Serve("/settings", static.LocalFile("./www", true)))
r.Use(static.Serve("/login", static.LocalFile("./www", true)))
r.Handle("GET", "/file/*filepath", Files)
r.Use(static.Serve("/", static.LocalFile(configDirectory+"/www", true)))
r.Use(static.Serve("/dashboard", static.LocalFile(configDirectory+"/www", true)))
r.Use(static.Serve("/media", static.LocalFile(configDirectory+"/www", true)))
r.Use(static.Serve("/settings", static.LocalFile(configDirectory+"/www", true)))
r.Use(static.Serve("/login", static.LocalFile(configDirectory+"/www", true)))
r.Handle("GET", "/file/*filepath", func(c *gin.Context) {
Files(c, configDirectory)
})
// Run the api on port
err = r.Run(":" + configuration.Port)
@@ -85,8 +87,8 @@ func StartServer(configuration *models.Configuration, communication *models.Comm
}
}
func Files(c *gin.Context) {
func Files(c *gin.Context, configDirectory string) {
c.Header("Access-Control-Allow-Origin", "*")
c.Header("Content-Type", "video/mp4")
c.File("./data/recordings" + c.Param("filepath"))
c.File(configDirectory + "/data/recordings" + c.Param("filepath"))
}

View File

@@ -5,6 +5,6 @@ import (
"github.com/kerberos-io/agent/machinery/src/routers/http"
)
func StartWebserver(configuration *models.Configuration, communication *models.Communication) {
http.StartServer(configuration, communication)
func StartWebserver(configDirectory string, configuration *models.Configuration, communication *models.Communication) {
http.StartServer(configDirectory, configuration, communication)
}

View File

@@ -110,15 +110,15 @@ func CountDigits(i int64) (count int) {
return count
}
func CheckDataDirectoryPermissions() error {
recordingsDirectory := "./data/recordings"
configDirectory := "./data/config"
snapshotsDirectory := "./data/snapshots"
cloudDirectory := "./data/cloud"
func CheckDataDirectoryPermissions(configDirectory string) error {
recordingsDirectory := configDirectory + "/data/recordings"
configurationDirectory := configDirectory + "/data/config"
snapshotsDirectory := configDirectory + "/data/snapshots"
cloudDirectory := configDirectory + "/data/cloud"
err := CheckDirectoryPermissions(recordingsDirectory)
if err == nil {
err = CheckDirectoryPermissions(configDirectory)
err = CheckDirectoryPermissions(configurationDirectory)
if err == nil {
err = CheckDirectoryPermissions(snapshotsDirectory)
if err == nil {

6
snap/hooks/configure vendored Normal file
View File

@@ -0,0 +1,6 @@
#!/bin/sh -e
cp -R $SNAP/data $SNAP_COMMON/
cp -R $SNAP/www $SNAP_COMMON/
cp -R $SNAP/version $SNAP_COMMON/
cp -R $SNAP/mp4fragment $SNAP_COMMON/

23
snap/snapcraft.yaml Normal file
View File

@@ -0,0 +1,23 @@
name: kerberosio # you probably want to 'snapcraft register <name>'
base: core22 # the base snap is the execution environment for this snap
version: '3.0.0' # just for humans, typically '1.2+git' or '1.3.2'
summary: A stand-alone open source video surveillance system # 79 char long summary
description: |
Kerberos Agent is an isolated and scalable video (surveillance) management
agent made available as Open Source under the MIT License. This means that
all the source code is available for you or your company, and you can use,
transform and distribute the source code; as long you keep a reference of
the original license. Kerberos Agent can be used for commercial usage.
grade: stable # stable # must be 'stable' to release into candidate/stable channels
confinement: strict # use 'strict' once you have the right plugs and slots
environment:
GIN_MODE: release
apps:
agent:
command: main -config /var/snap/kerberosio/common
plugs: [ network, network-bind ]
parts:
agent:
source: . #https://github.com/kerberos-io/agent/releases/download/21c0e01/agent-amd64.tar
plugin: dump

View File

@@ -9,9 +9,9 @@ const dev = {
ENV: 'dev',
// Comment the below lines, when using codespaces or other special DNS names (which you can't control)
HOSTNAME: hostname,
API_URL: `${protocol}//${hostname}:8080/api`,
URL: `${protocol}//${hostname}:8080`,
WS_URL: `${websocketprotocol}//${hostname}:8080/ws`,
API_URL: `${protocol}//${hostname}:80/api`,
URL: `${protocol}//${hostname}:80`,
WS_URL: `${websocketprotocol}//${hostname}:80/ws`,
MODE: window['env']['mode'],
// Uncomment, and comment the above lines, when using codespaces or other special DNS names (which you can't control)
// HOSTNAME: externalHost,