v0.4.0
ci/woodpecker/push/gitea Pipeline was successful Details
ci/woodpecker/push/go-lint Pipeline failed Details
ci/woodpecker/tag/gitea Pipeline failed Details
ci/woodpecker/tag/go-lint Pipeline failed Details
ci/woodpecker/tag/publish-docs Pipeline is running Details
ci/woodpecker/push/publish-docs Pipeline was successful Details

master
Andrew Woodlee 8 months ago
parent 6ccb75f4fa
commit a4214b2b3f

@ -0,0 +1,13 @@
## v0.4.0 - 2023-09-08
### Added
* Added `scriptEnvFile` to command object that allows one to specify an environment file (or any file really) when a `scriptFile` is run. Inspired by the practice of keeping environment variables and scripts or commands seperate.
* Basis for listing commands
### Changed
* BREAKING: Notifications object now takes the form of service.id, where service can be "mail" or "matrix" and id is a unique id for the service.
* BREAKING: Since the change to the notifications object, cmd-lists' inner map key 'notifications' must be of the form service.id. id must be defined for that service. See notifications docs for aviliable services.
* Config parser is now the simpler Koanf - Keys are now case-sensitive
* Log size limited to 50 Mb

@ -21,7 +21,7 @@ jobs:
- run: git fetch --force --tags - run: git fetch --force --tags
- uses: actions/setup-go@v4 - uses: actions/setup-go@v4
with: with:
go-version: '1.20' go-version: '1.26'
cache: true cache: true
# More assembly might be required: Docker logins, GPG, etc. It all depends # More assembly might be required: Docker logins, GPG, etc. It all depends
# on your needs. # on your needs.

@ -32,10 +32,10 @@ func Backup(cmd *cobra.Command, args []string) {
backyConfOpts := backy.NewOpts(cfgFile, backy.AddCommandLists(cmdLists)) backyConfOpts := backy.NewOpts(cfgFile, backy.AddCommandLists(cmdLists))
backyConfOpts.InitConfig() backyConfOpts.InitConfig()
config := backy.ReadConfig(backyConfOpts) backy.ReadConfig(backyConfOpts)
config.RunListConfig("", backyConfOpts) backyConfOpts.RunListConfig("")
for _, host := range config.Hosts { for _, host := range backyConfOpts.Hosts {
if host.SshClient != nil { if host.SshClient != nil {
host.SshClient.Close() host.SshClient.Close()
} }

@ -23,7 +23,7 @@ var (
func execute(cmd *cobra.Command, args []string) { func execute(cmd *cobra.Command, args []string) {
if len(args) < 1 { if len(args) < 1 {
logging.ExitWithMSG("Please provide a command to run. Pass --help to see options.", 0, nil) logging.ExitWithMSG("Please provide a command to run. Pass --help to see options.", 1, nil)
} }
opts := backy.NewOpts(cfgFile, backy.AddCommands(args)) opts := backy.NewOpts(cfgFile, backy.AddCommands(args))

@ -0,0 +1,39 @@
// backup.go
// Copyright (C) Andrew Woodlee 2023
// License: Apache-2.0
package cmd
import (
"git.andrewnw.xyz/CyberShell/backy/pkg/backy"
"github.com/spf13/cobra"
)
var (
listCmd = &cobra.Command{
Use: "list [--list=list1,list2,... | -l list1, list2,...] [ -cmd cmd1 cmd2 cmd3...]",
Short: "Lists commands, lists, or hosts defined in config file.",
Long: "Backup lists commands or groups defined in config file.\nUse the --lists or -l flag to list the specified lists. If not flag is not given, all lists will be executed.",
Run: List,
}
)
var listsToList []string
func init() {
listCmd.Flags().StringSliceVarP(&listsToList, "lists", "l", nil, "Accepts comma-separated names of command lists to list.")
}
func List(cmd *cobra.Command, args []string) {
opts := backy.NewOpts(cfgFile, backy.SetListsToSearch(cmdLists))
opts.InitConfig()
opts = backy.ReadConfig(opts)
opts.ListConfiguration()
}

@ -36,5 +36,5 @@ func init() {
rootCmd.PersistentFlags().StringVarP(&cfgFile, "config", "f", "", "config file to read from") rootCmd.PersistentFlags().StringVarP(&cfgFile, "config", "f", "", "config file to read from")
rootCmd.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, "Sets verbose level") rootCmd.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, "Sets verbose level")
rootCmd.AddCommand(backupCmd, execCmd, cronCmd, versionCmd) rootCmd.AddCommand(backupCmd, execCmd, cronCmd, versionCmd, listCmd)
} }

@ -7,7 +7,7 @@ import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
const versionStr = "0.3.1" const versionStr = "0.4.0"
var ( var (
versionCmd = &cobra.Command{ versionCmd = &cobra.Command{
@ -32,9 +32,6 @@ func version(cmd *cobra.Command, args []string) {
} else if vPre && !numOnly { } else if vPre && !numOnly {
fmt.Printf("v%s\n", versionStr) fmt.Printf("v%s\n", versionStr)
} else { } else {
if vPre && numOnly {
fmt.Println("vpre flag and num flag both detected!")
}
fmt.Printf("Backy version: %s\n", versionStr) fmt.Printf("Backy version: %s\n", versionStr)
} }

@ -19,6 +19,7 @@ Available Commands:
cron Starts a scheduler that runs lists defined in config file. cron Starts a scheduler that runs lists defined in config file.
exec Runs commands defined in config file in order given. exec Runs commands defined in config file in order given.
help Help about any command help Help about any command
list Lists commands, lists, or hosts defined in config file.
version Prints the version and exits version Prints the version and exits
Flags: Flags:
@ -91,7 +92,7 @@ Usage:
Flags: Flags:
-h, --help help for version -h, --help help for version
-n, --num Output the version number only. (default true) -n, --num Output the version number only.
-V, --vpre Output the version with v prefixed. -V, --vpre Output the version with v prefixed.
Global Flags: Global Flags:
@ -99,3 +100,20 @@ Global Flags:
-v, --verbose Sets verbose level -v, --verbose Sets verbose level
``` ```
## list
```
Backup lists commands or groups defined in config file.
Use the --lists or -l flag to list the specified lists. If not flag is not given, all lists will be executed.
Usage:
backy list [--list=list1,list2,... | -l list1, list2,...] [ -cmd cmd1 cmd2 cmd3...] [flags]
Flags:
-h, --help help for list
-l, --lists strings Accepts comma-separated names of command lists to list.
Global Flags:
-f, --config string config file to read from
-v, --verbose Sets verbose level
```

@ -16,8 +16,8 @@ The top-level object key can be anything you want but not the same as another.
- test - test
- test2 - test2
notifications: notifications:
- prod-email - mail.prod-email
- matrix - matrix.sysadmin
cron: "0 * * * * *" cron: "0 * * * * *"
``` ```
@ -25,9 +25,9 @@ The top-level object key can be anything you want but not the same as another.
| --- | --- | --- | --- | | --- | --- | --- | --- |
| `order` | Defines the sequence of commands to execute | `[]string` | yes | | `order` | Defines the sequence of commands to execute | `[]string` | yes |
| `getOutput` | Command(s) output is in the notification(s) | `bool` | no | | `getOutput` | Command(s) output is in the notification(s) | `bool` | no |
| `notifications` | The notification IDs to use on success and failure | `[]string` | no | | `notifications` | The notification service(s) and ID(s) to use on success and failure. Must be *`service.id`*. See the [notifications documentation page](/config/notifications/) for more | `[]string` | no |
| `name` | Optional name of the list | `string` | no | | `name` | Optional name of the list | `string` | no |
| `cron` | Time at which to schedule the list. | `string` | no | | `cron` | Time at which to schedule the list. Only has affect when cron subcommand is run. | `string` | no |
### Order ### Order
@ -43,7 +43,7 @@ order:
Get command output when a notification is sent. Get command output when a notification is sent.
Is not required. Can be `true` or `false`. Is not required. Can be `true` or `false`. Default is `false`.
### Notifications ### Notifications
@ -57,29 +57,30 @@ Name is optional for logging. If name is not defined, name will be the object's
Backy also has a cron mode, so one can run `backy cron` and start a process that schedules jobs to run at times defined in the configuration file. Backy also has a cron mode, so one can run `backy cron` and start a process that schedules jobs to run at times defined in the configuration file.
Adding `cron: 0 0 1 * * *` to a `cmd-configs` object will schedule the list at 1 in the morning. See [https://crontab.guru/](https://crontab.guru/) for reference. Adding `cron: 0 0 1 * * *` to a `cmd-lists` object will schedule the list at 1 in the morning. See [https://crontab.guru/](https://crontab.guru/) for reference.
{{% notice tip %}} {{% notice tip %}}
Note: Backy uses the second field of cron, so add anything except * to the beginning of a regular cron expression. Note: Backy uses the second field of cron, so add anything except * to the beginning of a regular cron expression.
{{% /notice %}} {{% /notice %}}
```yaml ```yaml
cmd-configs: cmd-lists:
  cmds-to-run: # this can be any name you want   docker-container-backup: # this can be any name you want
    # all commands have to be defined     # all commands have to be defined
    order:     order:
      - stop-docker-container       - stop-docker-container
      - backup-docker-container-script       - backup-docker-container-script
      - shell-cmd       - shell-cmd
      - hostname       - hostname
      - start-docker-container
    notifications:     notifications:
      - matrix       - matrix.id
    name: backup-some-server     name: backup-some-container
    cron: "0 0 1 * * *"     cron: "0 0 1 * * *"
  hostname:   hostname:
    name: hostname     name: hostname
    order:     order:
      - hostname       - hostname
    notifications:     notifications:
    - prod-email     - mail.prod-email
``` ```

@ -10,33 +10,33 @@ Notifications can be sent on command list completion and failure.
The supported platforms for notifications are email (SMTP) and [Matrix](https://matrix.org/). The supported platforms for notifications are email (SMTP) and [Matrix](https://matrix.org/).
Notifications are defined by type. The top-level object will be the id, and the `type` is required. Notifications are defined by service, with the current form following below. Ids must come after the service.
```yaml ```yaml
notifications: notifications:
prod-email: mail:
type: mail prod-email:
host: yourhost.tld host: yourhost.tld
port: 587 port: 587
senderaddress: email@domain.tld senderaddress: email@domain.tld
to: to:
- admin@domain.tld - admin@domain.tld
username: smtp-username@domain.tld username: smtp-username@domain.tld
password: your-password-here password: your-password-here
matrix: matrix:
type: matrix matrix:
home-server: your-home-server.tld home-server: your-home-server.tld
room-id: room-id room-id: room-id
access-token: your-access-token access-token: your-access-token
user-id: your-user-id user-id: your-user-id
``` ```
Types recognized are `type: mail` and `type: matrix` Sections recognized are `mail` and `matrix`
The type's object and its keys are listed below. There must be a section with an id (eg. `mail.test-svr`) following one of these sections.
### type: mail ### mail
| key | description | type | key | description | type
| --- | --- | --- | --- | --- | ---
@ -47,7 +47,7 @@ The type's object and its keys are listed below.
| `username` | SMTP username | `string` | `username` | SMTP username | `string`
| `password` | SMTP password | `string` | `password` | SMTP password | `string`
### type: matrix ### matrix
| key | description | type | key | description | type
| --- | --- | --- | --- | --- | ---

@ -51,17 +51,17 @@
"frontMatter.content.pageFolders": [ "frontMatter.content.pageFolders": [
{ {
"title": "content", "title": "content",
"path": "[[workspace]]/content", "path": "[[workspace]]/docs/content",
"originalPath": "[[workspace]]/content" "originalPath": "[[workspace]]/docs/content"
}, },
{ {
"title": "config-main", "title": "config-main",
"path": "[[workspace]]/content/config", "path": "[[workspace]]/docs/content/config",
"originalPath": "[[workspace]]/content/config" "originalPath": "[[workspace]]/docs/content/config"
}, },
{ {
"title": "gs", "title": "gs",
"path": "[[workspace]]/content/getting-started" "path": "[[workspace]]/docs/content/getting-started"
} }
] ]
} }

@ -50,4 +50,11 @@ eval "${BACKYCOMMAND} version -h >> _index.md"
echo "\`\`\`" >> _index.md echo "\`\`\`" >> _index.md
echo "" >> _index.md echo "" >> _index.md
echo "## list" >> _index.md
echo "" >> _index.md
echo "\`\`\`" >> _index.md
eval "${BACKYCOMMAND} list -h >> _index.md"
echo "\`\`\`" >> _index.md
mv _index.md "$CLI_PAGE" mv _index.md "$CLI_PAGE"

@ -3,29 +3,33 @@ module git.andrewnw.xyz/CyberShell/backy
go 1.26 go 1.26
require ( require (
github.com/go-co-op/gocron v1.30.1 github.com/go-co-op/gocron v1.33.1
github.com/hashicorp/vault/api v1.9.2 github.com/hashicorp/vault/api v1.10.0
github.com/joho/godotenv v1.5.1 github.com/joho/godotenv v1.5.1
github.com/kevinburke/ssh_config v1.2.0 github.com/kevinburke/ssh_config v1.2.0
github.com/knadh/koanf/parsers/yaml v0.1.0
github.com/knadh/koanf/providers/file v0.1.0
github.com/knadh/koanf/v2 v2.0.1
github.com/mattn/go-isatty v0.0.19 github.com/mattn/go-isatty v0.0.19
github.com/nikoksr/notify v0.41.0 github.com/nikoksr/notify v0.41.0
github.com/pkg/errors v0.9.1 github.com/pkg/errors v0.9.1
github.com/rs/zerolog v1.30.0 github.com/rs/zerolog v1.30.0
github.com/spf13/cobra v1.7.0 github.com/spf13/cobra v1.7.0
github.com/spf13/viper v1.16.0 go.mongodb.org/mongo-driver v1.12.1
go.mongodb.org/mongo-driver v1.12.0 golang.org/x/crypto v0.13.0
golang.org/x/crypto v0.11.0
gopkg.in/natefinch/lumberjack.v2 v2.2.1 gopkg.in/natefinch/lumberjack.v2 v2.2.1
maunium.net/go/mautrix v0.15.4 maunium.net/go/mautrix v0.16.0
mvdan.cc/sh/v3 v3.7.0 mvdan.cc/sh/v3 v3.7.0
) )
require ( require (
github.com/cenkalti/backoff/v3 v3.2.2 // indirect github.com/cenkalti/backoff/v3 v3.2.2 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect
github.com/fatih/structs v1.1.0 // indirect
github.com/fsnotify/fsnotify v1.6.0 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect
github.com/go-jose/go-jose/v3 v3.0.0 // indirect github.com/go-jose/go-jose/v3 v3.0.0 // indirect
github.com/golang/snappy v0.0.4 // indirect github.com/golang/snappy v0.0.4 // indirect
github.com/google/uuid v1.3.1 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect
@ -33,17 +37,21 @@ require (
github.com/hashicorp/go-rootcerts v1.0.2 // indirect github.com/hashicorp/go-rootcerts v1.0.2 // indirect
github.com/hashicorp/go-secure-stdlib/parseutil v0.1.7 // indirect github.com/hashicorp/go-secure-stdlib/parseutil v0.1.7 // indirect
github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 // indirect github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 // indirect
github.com/hashicorp/go-sockaddr v1.0.2 // indirect github.com/hashicorp/go-sockaddr v1.0.5 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect github.com/hashicorp/hcl v1.0.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible // indirect github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible // indirect
github.com/klauspost/compress v1.16.7 // indirect github.com/klauspost/compress v1.16.7 // indirect
github.com/knadh/koanf/maps v0.1.1 // indirect
github.com/knadh/koanf/providers/structs v0.1.0 // indirect
github.com/magiconair/properties v1.8.7 // indirect github.com/magiconair/properties v1.8.7 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/montanaflynn/stats v0.7.1 // indirect github.com/montanaflynn/stats v0.7.1 // indirect
github.com/pelletier/go-toml/v2 v2.0.9 // indirect github.com/pelletier/go-toml/v2 v2.1.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/robfig/cron/v3 v3.0.1 // indirect github.com/robfig/cron/v3 v3.0.1 // indirect
github.com/ryanuber/go-glob v1.0.0 // indirect github.com/ryanuber/go-glob v1.0.0 // indirect
@ -51,10 +59,10 @@ require (
github.com/spf13/cast v1.5.1 // indirect github.com/spf13/cast v1.5.1 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect github.com/spf13/pflag v1.0.5 // indirect
github.com/stretchr/objx v0.5.0 // indirect github.com/stretchr/objx v0.5.1 // indirect
github.com/stretchr/testify v1.8.4 // indirect github.com/stretchr/testify v1.8.4 // indirect
github.com/subosito/gotenv v1.4.2 // indirect github.com/subosito/gotenv v1.6.0 // indirect
github.com/tidwall/gjson v1.15.0 // indirect github.com/tidwall/gjson v1.16.0 // indirect
github.com/tidwall/match v1.1.1 // indirect github.com/tidwall/match v1.1.1 // indirect
github.com/tidwall/pretty v1.2.1 // indirect github.com/tidwall/pretty v1.2.1 // indirect
github.com/tidwall/sjson v1.2.5 // indirect github.com/tidwall/sjson v1.2.5 // indirect
@ -62,12 +70,13 @@ require (
github.com/xdg-go/scram v1.1.2 // indirect github.com/xdg-go/scram v1.1.2 // indirect
github.com/xdg-go/stringprep v1.0.4 // indirect github.com/xdg-go/stringprep v1.0.4 // indirect
github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a // indirect github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a // indirect
go.uber.org/atomic v1.9.0 // indirect go.mau.fi/util v0.0.0-20230906155759-14bad39a8718 // indirect
golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1 // indirect go.uber.org/atomic v1.11.0 // indirect
golang.org/x/net v0.12.0 // indirect golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect
golang.org/x/net v0.15.0 // indirect
golang.org/x/sync v0.3.0 // indirect golang.org/x/sync v0.3.0 // indirect
golang.org/x/sys v0.10.0 // indirect golang.org/x/sys v0.12.0 // indirect
golang.org/x/text v0.11.0 // indirect golang.org/x/text v0.13.0 // indirect
golang.org/x/time v0.3.0 // indirect golang.org/x/time v0.3.0 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect

@ -64,11 +64,15 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.m
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
github.com/frankban/quicktest v1.14.5 h1:dfYrrRyLtiqT9GyKXgdh+k4inNeTvmGbuSgZ3lx3GhA= github.com/frankban/quicktest v1.14.5 h1:dfYrrRyLtiqT9GyKXgdh+k4inNeTvmGbuSgZ3lx3GhA=
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
github.com/go-co-op/gocron v1.30.1 h1:tjWUvJl5KrcwpkEkSXFSQFr4F9h5SfV/m4+RX0cV2fs= github.com/go-co-op/gocron v1.30.1 h1:tjWUvJl5KrcwpkEkSXFSQFr4F9h5SfV/m4+RX0cV2fs=
github.com/go-co-op/gocron v1.30.1/go.mod h1:39f6KNSGVOU1LO/ZOoZfcSxwlsJDQOKSu8erN0SH48Y= github.com/go-co-op/gocron v1.30.1/go.mod h1:39f6KNSGVOU1LO/ZOoZfcSxwlsJDQOKSu8erN0SH48Y=
github.com/go-co-op/gocron v1.33.1 h1:wjX+Dg6Ae29a/f9BSQjY1Rl+jflTpW9aDyMqseCj78c=
github.com/go-co-op/gocron v1.33.1/go.mod h1:NLi+bkm4rRSy1F8U7iacZOz0xPseMoIOnvabGoSe/no=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
@ -131,6 +135,8 @@ github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLe
github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4=
github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=
@ -155,12 +161,16 @@ github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 h1:kes8mmyCpxJsI7FTwtzRqEy9
github.com/hashicorp/go-secure-stdlib/strutil v0.1.2/go.mod h1:Gou2R9+il93BqX25LAKCLuM+y9U2T4hlwvT1yprcna4= github.com/hashicorp/go-secure-stdlib/strutil v0.1.2/go.mod h1:Gou2R9+il93BqX25LAKCLuM+y9U2T4hlwvT1yprcna4=
github.com/hashicorp/go-sockaddr v1.0.2 h1:ztczhD1jLxIRjVejw8gFomI1BQZOe2WoVOu0SyteCQc= github.com/hashicorp/go-sockaddr v1.0.2 h1:ztczhD1jLxIRjVejw8gFomI1BQZOe2WoVOu0SyteCQc=
github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A= github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A=
github.com/hashicorp/go-sockaddr v1.0.5 h1:dvk7TIXCZpmfOlM+9mlcrWmWjw/wlKT+VDq2wMvfPJU=
github.com/hashicorp/go-sockaddr v1.0.5/go.mod h1:uoUUmtwU7n9Dv3O4SNLeFvg0SxQ3lyjsj6+CCykpaxI=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hashicorp/vault/api v1.9.2 h1:YjkZLJ7K3inKgMZ0wzCU9OHqc+UqMQyXsPXnf3Cl2as= github.com/hashicorp/vault/api v1.9.2 h1:YjkZLJ7K3inKgMZ0wzCU9OHqc+UqMQyXsPXnf3Cl2as=
github.com/hashicorp/vault/api v1.9.2/go.mod h1:jo5Y/ET+hNyz+JnKDt8XLAdKs+AM0G5W0Vp1IrFI8N8= github.com/hashicorp/vault/api v1.9.2/go.mod h1:jo5Y/ET+hNyz+JnKDt8XLAdKs+AM0G5W0Vp1IrFI8N8=
github.com/hashicorp/vault/api v1.10.0 h1:/US7sIjWN6Imp4o/Rj1Ce2Nr5bki/AXi9vAW3p2tOJQ=
github.com/hashicorp/vault/api v1.10.0/go.mod h1:jo5Y/ET+hNyz+JnKDt8XLAdKs+AM0G5W0Vp1IrFI8N8=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
@ -177,6 +187,16 @@ github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+o
github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
github.com/klauspost/compress v1.16.7 h1:2mk3MPGNzKyxErAw8YaohYh69+pa4sIQSC0fPGCFR9I= github.com/klauspost/compress v1.16.7 h1:2mk3MPGNzKyxErAw8YaohYh69+pa4sIQSC0fPGCFR9I=
github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
github.com/knadh/koanf/maps v0.1.1 h1:G5TjmUh2D7G2YWf5SQQqSiHRJEjaicvU0KpypqB3NIs=
github.com/knadh/koanf/maps v0.1.1/go.mod h1:npD/QZY3V6ghQDdcQzl1W4ICNVTkohC8E73eI2xW4yI=
github.com/knadh/koanf/parsers/yaml v0.1.0 h1:ZZ8/iGfRLvKSaMEECEBPM1HQslrZADk8fP1XFUxVI5w=
github.com/knadh/koanf/parsers/yaml v0.1.0/go.mod h1:cvbUDC7AL23pImuQP0oRw/hPuccrNBS2bps8asS0CwY=
github.com/knadh/koanf/providers/file v0.1.0 h1:fs6U7nrV58d3CFAFh8VTde8TM262ObYf3ODrc//Lp+c=
github.com/knadh/koanf/providers/file v0.1.0/go.mod h1:rjJ/nHQl64iYCtAW2QQnF0eSmDEX/YZ/eNFj5yR6BvA=
github.com/knadh/koanf/providers/structs v0.1.0 h1:wJRteCNn1qvLtE5h8KQBvLJovidSdntfdyIbbCzEyE0=
github.com/knadh/koanf/providers/structs v0.1.0/go.mod h1:sw2YZ3txUcqA3Z27gPlmmBzWn1h8Nt9O6EP/91MkcWE=
github.com/knadh/koanf/v2 v2.0.1 h1:1dYGITt1I23x8cfx8ZnldtezdyaZtfAuRtIFOiRzK7g=
github.com/knadh/koanf/v2 v2.0.1/go.mod h1:ZeiIlIDXTE7w1lMT6UVcNiRAS2/rCeLn/GdLNvY1Dus=
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
@ -198,12 +218,16 @@ github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
github.com/montanaflynn/stats v0.7.1 h1:etflOAAHORrCC44V+aR6Ftzort912ZU+YLiSTuV8eaE= github.com/montanaflynn/stats v0.7.1 h1:etflOAAHORrCC44V+aR6Ftzort912ZU+YLiSTuV8eaE=
github.com/montanaflynn/stats v0.7.1/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow= github.com/montanaflynn/stats v0.7.1/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow=
@ -211,6 +235,8 @@ github.com/nikoksr/notify v0.41.0 h1:4LGE41GpWdHX5M3Xo6DlWRwS2WLDbOq1Rk7IzY4vjmQ
github.com/nikoksr/notify v0.41.0/go.mod h1:FoE0UVPeopz1Vy5nm9vQZ+JVmYjEIjQgbFstbkw+cRE= github.com/nikoksr/notify v0.41.0/go.mod h1:FoE0UVPeopz1Vy5nm9vQZ+JVmYjEIjQgbFstbkw+cRE=
github.com/pelletier/go-toml/v2 v2.0.9 h1:uH2qQXheeefCCkuBBSLi7jCiSmj3VRh2+Goq2N7Xxu0= github.com/pelletier/go-toml/v2 v2.0.9 h1:uH2qQXheeefCCkuBBSLi7jCiSmj3VRh2+Goq2N7Xxu0=
github.com/pelletier/go-toml/v2 v2.0.9/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= github.com/pelletier/go-toml/v2 v2.0.9/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4=
github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
@ -248,6 +274,8 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.1 h1:4VhoImhV/Bm0ToFkXFi8hXNXwpDRZ/ynw3amt82mzq0=
github.com/stretchr/objx v0.5.1/go.mod h1:/iHQpkQwBD6DLUmQ4pE+s1TXdob1mORJ4/UFdrifcy0=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 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/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
@ -261,9 +289,13 @@ github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcU
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8= github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8=
github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0=
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/gjson v1.15.0 h1:5n/pM+v3r5ujuNl4YLZLsQ+UE5jlkLVm7jMzT5Mpolw= github.com/tidwall/gjson v1.15.0 h1:5n/pM+v3r5ujuNl4YLZLsQ+UE5jlkLVm7jMzT5Mpolw=
github.com/tidwall/gjson v1.15.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/gjson v1.15.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/gjson v1.16.0 h1:SyXa+dsSPpUlcwEDuKuEBJEz5vzTvOea+9rjyYodQFg=
github.com/tidwall/gjson v1.16.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
@ -285,8 +317,14 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
go.mau.fi/util v0.0.0-20230805171708-199bf3eec776 h1:VrxDCO/gLFHLQywGUsJzertrvt2mUEMrZPf4hEL/s18=
go.mau.fi/util v0.0.0-20230805171708-199bf3eec776/go.mod h1:AxuJUMCxpzgJ5eV9JbPWKRH8aAJJidxetNdUj7qcb84=
go.mau.fi/util v0.0.0-20230906155759-14bad39a8718 h1:hmm5bZqE0M8+Uvys0HJPCSbAIZIwYtTkBKYPjAWHuMM=
go.mau.fi/util v0.0.0-20230906155759-14bad39a8718/go.mod h1:AxuJUMCxpzgJ5eV9JbPWKRH8aAJJidxetNdUj7qcb84=
go.mongodb.org/mongo-driver v1.12.0 h1:aPx33jmn/rQuJXPQLZQ8NtfPQG8CaqgLThFtqRb0PiE= go.mongodb.org/mongo-driver v1.12.0 h1:aPx33jmn/rQuJXPQLZQ8NtfPQG8CaqgLThFtqRb0PiE=
go.mongodb.org/mongo-driver v1.12.0/go.mod h1:AZkxhPnFJUoH7kZlFkVKucV20K387miPfm7oimrSmK0= go.mongodb.org/mongo-driver v1.12.0/go.mod h1:AZkxhPnFJUoH7kZlFkVKucV20K387miPfm7oimrSmK0=
go.mongodb.org/mongo-driver v1.12.1 h1:nLkghSU8fQNaK7oUmDhQFsnrtcoNy7Z6LVFKsEecqgE=
go.mongodb.org/mongo-driver v1.12.1/go.mod h1:/rGBTebI3XYboVmgz+Wv3Bcbl3aD0QF9zl6kDDw18rQ=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
@ -295,6 +333,8 @@ go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 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/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
@ -308,6 +348,8 @@ golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.11.0 h1:6Ewdq3tDic1mg5xRO4milcWCfMVQhI4NkqWWvqejpuA= golang.org/x/crypto v0.11.0 h1:6Ewdq3tDic1mg5xRO4milcWCfMVQhI4NkqWWvqejpuA=
golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio= golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio=
golang.org/x/crypto v0.13.0 h1:mvySKfSWJ+UKUii46M40LOvyWfN0s2U+46/jDd0e6Ck=
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@ -320,6 +362,8 @@ golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EH
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1 h1:MGwJjxBy0HJshjDNfLsYO8xppfqWlA5ZT9OhtUUhTNw= golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1 h1:MGwJjxBy0HJshjDNfLsYO8xppfqWlA5ZT9OhtUUhTNw=
golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc= golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc=
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g=
golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
@ -379,6 +423,8 @@ golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qx
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.12.0 h1:cfawfvKITfUsFCeJIHJrbSxpeu/E81khclypR0GVT50= golang.org/x/net v0.12.0 h1:cfawfvKITfUsFCeJIHJrbSxpeu/E81khclypR0GVT50=
golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA=
golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8=
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@ -446,6 +492,10 @@ golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA= golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA=
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM=
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.10.0 h1:3R7pNqamzBraeqj/Tj8qt1aQ2HpmlC+Cx/qL/7hn4/c= golang.org/x/term v0.10.0 h1:3R7pNqamzBraeqj/Tj8qt1aQ2HpmlC+Cx/qL/7hn4/c=
@ -461,6 +511,8 @@ golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4= golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4=
golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@ -630,6 +682,8 @@ maunium.net/go/maulogger/v2 v2.4.1 h1:N7zSdd0mZkB2m2JtFUsiGTQQAdP0YeFWT7YMc80yAL
maunium.net/go/maulogger/v2 v2.4.1/go.mod h1:omPuYwYBILeVQobz8uO3XC8DIRuEb5rXYlQSuqrbCho= maunium.net/go/maulogger/v2 v2.4.1/go.mod h1:omPuYwYBILeVQobz8uO3XC8DIRuEb5rXYlQSuqrbCho=
maunium.net/go/mautrix v0.15.4 h1:Ug3n2Mo+9Yb94AjZTWJQSNHmShaksEzZi85EPl3S3P0= maunium.net/go/mautrix v0.15.4 h1:Ug3n2Mo+9Yb94AjZTWJQSNHmShaksEzZi85EPl3S3P0=
maunium.net/go/mautrix v0.15.4/go.mod h1:dBaDmsnOOBM4a+gKcgefXH73pHGXm+MCJzCs1dXFgrw= maunium.net/go/mautrix v0.15.4/go.mod h1:dBaDmsnOOBM4a+gKcgefXH73pHGXm+MCJzCs1dXFgrw=
maunium.net/go/mautrix v0.16.0 h1:iUqCzJE2yqBC1ddAK6eAn159My8rLb4X8g4SFtQh2Dk=
maunium.net/go/mautrix v0.16.0/go.mod h1:XAjE9pTSGcr6vXaiNgQGiip7tddJ8FQV1a29u2QdBG4=
mvdan.cc/sh/v3 v3.7.0 h1:lSTjdP/1xsddtaKfGg7Myu7DnlHItd3/M2tomOcNNBg= mvdan.cc/sh/v3 v3.7.0 h1:lSTjdP/1xsddtaKfGg7Myu7DnlHItd3/M2tomOcNNBg=
mvdan.cc/sh/v3 v3.7.0/go.mod h1:K2gwkaesF/D7av7Kxl0HbF5kGOd2ArupNTX3X44+8l8= mvdan.cc/sh/v3 v3.7.0/go.mod h1:K2gwkaesF/D7av7Kxl0HbF5kGOd2ArupNTX3X44+8l8=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=

@ -17,12 +17,13 @@ import (
"embed" "embed"
"github.com/rs/zerolog" "github.com/rs/zerolog"
"github.com/rs/zerolog/log"
) )
//go:embed templates/*.txt //go:embed templates/*.txt
var templates embed.FS var templates embed.FS
var requiredKeys = []string{"commands", "cmd-configs"} var requiredKeys = []string{"commands"}
var Sprintf = fmt.Sprintf var Sprintf = fmt.Sprintf
@ -30,7 +31,7 @@ var Sprintf = fmt.Sprintf
// The environment of local commands will be the machine's environment plus any extra // The environment of local commands will be the machine's environment plus any extra
// variables specified in the Env file or Environment. // variables specified in the Env file or Environment.
// Dir can also be specified for local commands. // Dir can also be specified for local commands.
func (command *Command) RunCmd(log zerolog.Logger, backyConf *ConfigFile, opts *ConfigOpts) ([]string, error) { func (command *Command) RunCmd(cmdCtxLogger zerolog.Logger, opts *ConfigOpts) ([]string, error) {
var ( var (
outputArr []string outputArr []string
@ -52,13 +53,13 @@ func (command *Command) RunCmd(log zerolog.Logger, backyConf *ConfigFile, opts *
command.Type = strings.TrimSpace(command.Type) command.Type = strings.TrimSpace(command.Type)
if command.Type != "" { if command.Type != "" {
log.Info().Str("Command", fmt.Sprintf("Running script %s on host %s", command.Cmd, *command.Host)).Send() cmdCtxLogger.Info().Str("Command", fmt.Sprintf("Running script %s on host %s", command.Cmd, *command.Host)).Send()
} else { } else {
log.Info().Str("Command", fmt.Sprintf("Running command %s %s on host %s", command.Cmd, ArgsStr, *command.Host)).Send() cmdCtxLogger.Info().Str("Command", fmt.Sprintf("Running command %s %s on host %s", command.Cmd, ArgsStr, *command.Host)).Send()
} }
if command.RemoteHost.SshClient == nil { if command.RemoteHost.SshClient == nil {
err := command.RemoteHost.ConnectToSSHHost(opts, backyConf) err := command.RemoteHost.ConnectToSSHHost(opts)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -67,7 +68,7 @@ func (command *Command) RunCmd(log zerolog.Logger, backyConf *ConfigFile, opts *
// Retry connecting to host; if that fails, error. If it does not fail, try to create new session // Retry connecting to host; if that fails, error. If it does not fail, try to create new session
if err != nil { if err != nil {
connErr := command.RemoteHost.ConnectToSSHHost(opts, backyConf) connErr := command.RemoteHost.ConnectToSSHHost(opts)
if connErr != nil { if connErr != nil {
return nil, fmt.Errorf("error creating session: %v, and error creating new connection to host: %v", err, connErr) return nil, fmt.Errorf("error creating session: %v, and error creating new connection to host: %v", err, connErr)
} }
@ -80,7 +81,7 @@ func (command *Command) RunCmd(log zerolog.Logger, backyConf *ConfigFile, opts *
defer commandSession.Close() defer commandSession.Close()
injectEnvIntoSSH(envVars, commandSession, opts, log) injectEnvIntoSSH(envVars, commandSession, opts, cmdCtxLogger)
cmd := command.Cmd cmd := command.Cmd
for _, a := range command.Args { for _, a := range command.Args {
cmd += " " + a cmd += " " + a
@ -98,7 +99,7 @@ func (command *Command) RunCmd(log zerolog.Logger, backyConf *ConfigFile, opts *
// did the program panic while writing to the buffer? // did the program panic while writing to the buffer?
defer func() { defer func() {
if err := recover(); err != nil { if err := recover(); err != nil {
log.Info().Msg(fmt.Sprintf("panic occured writing to buffer: %x", err)) cmdCtxLogger.Info().Msg(fmt.Sprintf("panic occured writing to buffer: %x", err))
} }
}() }()
@ -119,7 +120,7 @@ func (command *Command) RunCmd(log zerolog.Logger, backyConf *ConfigFile, opts *
if str, ok := outMap["output"].(string); ok { if str, ok := outMap["output"].(string); ok {
outputArr = append(outputArr, str) outputArr = append(outputArr, str)
} }
log.Info().Fields(outMap).Send() cmdCtxLogger.Info().Fields(outMap).Send()
} }
return outputArr, err return outputArr, err
} }
@ -211,7 +212,7 @@ func (command *Command) RunCmd(log zerolog.Logger, backyConf *ConfigFile, opts *
if str, ok := outMap["output"].(string); ok { if str, ok := outMap["output"].(string); ok {
outputArr = append(outputArr, str) outputArr = append(outputArr, str)
} }
log.Info().Fields(outMap).Send() cmdCtxLogger.Info().Fields(outMap).Send()
} }
return outputArr, err return outputArr, err
} }
@ -224,7 +225,7 @@ func (command *Command) RunCmd(log zerolog.Logger, backyConf *ConfigFile, opts *
if str, ok := outMap["output"].(string); ok { if str, ok := outMap["output"].(string); ok {
outputArr = append(outputArr, str) outputArr = append(outputArr, str)
} }
log.Info().Fields(outMap).Send() cmdCtxLogger.Info().Fields(outMap).Send()
} }
return outputArr, nil return outputArr, nil
} }
@ -240,18 +241,18 @@ func (command *Command) RunCmd(log zerolog.Logger, backyConf *ConfigFile, opts *
if str, ok := outMap["output"].(string); ok { if str, ok := outMap["output"].(string); ok {
outputArr = append(outputArr, str) outputArr = append(outputArr, str)
} }
log.Info().Fields(outMap).Send() cmdCtxLogger.Info().Fields(outMap).Send()
} }
if err != nil { if err != nil {
log.Error().Err(fmt.Errorf("error when running cmd: %s: %w", command.Cmd, err)).Send() cmdCtxLogger.Error().Err(fmt.Errorf("error when running cmd: %s: %w", command.Cmd, err)).Send()
return outputArr, err return outputArr, err
} }
} else { } else {
var err error var err error
if command.Shell != "" { if command.Shell != "" {
log.Info().Str("Command", fmt.Sprintf("Running command %s %s on local machine in %s", command.Cmd, ArgsStr, command.Shell)).Send() cmdCtxLogger.Info().Str("Command", fmt.Sprintf("Running command %s %s on local machine in %s", command.Cmd, ArgsStr, command.Shell)).Send()
ArgsStr = fmt.Sprintf("%s %s", command.Cmd, ArgsStr) ArgsStr = fmt.Sprintf("%s %s", command.Cmd, ArgsStr)
@ -260,7 +261,7 @@ func (command *Command) RunCmd(log zerolog.Logger, backyConf *ConfigFile, opts *
if command.Dir != nil { if command.Dir != nil {
localCMD.Dir = *command.Dir localCMD.Dir = *command.Dir
} }
injectEnvIntoLocalCMD(envVars, localCMD, log) injectEnvIntoLocalCMD(envVars, localCMD, cmdCtxLogger)
cmdOutWriters = io.MultiWriter(&cmdOutBuf) cmdOutWriters = io.MultiWriter(&cmdOutBuf)
@ -286,12 +287,12 @@ func (command *Command) RunCmd(log zerolog.Logger, backyConf *ConfigFile, opts *
} }
if err != nil { if err != nil {
log.Error().Err(fmt.Errorf("error when running cmd %s: %w", command.Cmd, err)).Send() cmdCtxLogger.Error().Err(fmt.Errorf("error when running cmd %s: %w", command.Cmd, err)).Send()
return outputArr, err return outputArr, err
} }
return outputArr, nil return outputArr, nil
} }
log.Info().Str("Command", fmt.Sprintf("Running command %s %s on local machine", command.Cmd, ArgsStr)).Send() cmdCtxLogger.Info().Str("Command", fmt.Sprintf("Running command %s %s on local machine", command.Cmd, ArgsStr)).Send()
localCMD := exec.Command(command.Cmd, command.Args...) localCMD := exec.Command(command.Cmd, command.Args...)
if command.Dir != nil { if command.Dir != nil {
@ -299,7 +300,7 @@ func (command *Command) RunCmd(log zerolog.Logger, backyConf *ConfigFile, opts *
} }
// fmt.Printf("%v\n", envVars.env) // fmt.Printf("%v\n", envVars.env)
injectEnvIntoLocalCMD(envVars, localCMD, log) injectEnvIntoLocalCMD(envVars, localCMD, cmdCtxLogger)
cmdOutWriters = io.MultiWriter(&cmdOutBuf) cmdOutWriters = io.MultiWriter(&cmdOutBuf)
// fmt.Printf("%v\n", localCMD.Environ()) // fmt.Printf("%v\n", localCMD.Environ())
@ -318,46 +319,46 @@ func (command *Command) RunCmd(log zerolog.Logger, backyConf *ConfigFile, opts *
if str, ok := outMap["output"].(string); ok { if str, ok := outMap["output"].(string); ok {
outputArr = append(outputArr, str) outputArr = append(outputArr, str)
} }
log.Info().Fields(outMap).Send() cmdCtxLogger.Info().Fields(outMap).Send()
} }
if err != nil { if err != nil {
log.Error().Err(fmt.Errorf("error when running cmd %s: %w", command.Cmd, err)).Send() cmdCtxLogger.Error().Err(fmt.Errorf("error when running cmd %s: %w", command.Cmd, err)).Send()
return outputArr, err return outputArr, err
} }
} }
return outputArr, nil return outputArr, nil
} }
func cmdListWorker(msgTemps *msgTemplates, jobs <-chan *CmdList, config *ConfigFile, results chan<- string, opts *ConfigOpts) { func cmdListWorker(msgTemps *msgTemplates, jobs <-chan *CmdList, results chan<- string, opts *ConfigOpts) {
for list := range jobs { for list := range jobs {
fieldsMap := make(map[string]interface{}) fieldsMap := make(map[string]interface{})
fieldsMap["list"] = list.Name fieldsMap["list"] = list.Name
cmdLog := config.Logger.Info() cmdLog := opts.Logger.Info()
var count int var count int
var cmdsRan []string var cmdsRan []string
var outStructArr []outStruct var outStructArr []outStruct
for _, cmd := range list.Order { for _, cmd := range list.Order {
currentCmd := config.Cmds[cmd].Cmd currentCmd := opts.Cmds[cmd].Cmd
fieldsMap["cmd"] = config.Cmds[cmd].Cmd fieldsMap["cmd"] = opts.Cmds[cmd].Cmd
cmdToRun := config.Cmds[cmd] cmdToRun := opts.Cmds[cmd]
cmdLog.Fields(fieldsMap).Send() cmdLog.Fields(fieldsMap).Send()
cmdLogger := config.Logger.With(). cmdLogger := opts.Logger.With().
Str("backy-cmd", cmd).Str("Host", "local machine"). Str("backy-cmd", cmd).Str("Host", "local machine").
Logger() Logger()
if cmdToRun.Host != nil { if cmdToRun.Host != nil {
cmdLogger = config.Logger.With(). cmdLogger = opts.Logger.With().
Str("backy-cmd", cmd).Str("Host", *cmdToRun.Host). Str("backy-cmd", cmd).Str("Host", *cmdToRun.Host).
Logger() Logger()
} }
outputArr, runOutErr := cmdToRun.RunCmd(cmdLogger, config, opts) outputArr, runOutErr := cmdToRun.RunCmd(cmdLogger, opts)
if list.NotifyConfig != nil { if list.NotifyConfig != nil {
if cmdToRun.GetOutput || list.GetOutput { if cmdToRun.GetOutput || list.GetOutput {
@ -380,7 +381,7 @@ func cmdListWorker(msgTemps *msgTemplates, jobs <-chan *CmdList, config *ConfigF
errStruct["listName"] = list.Name errStruct["listName"] = list.Name
errStruct["Command"] = currentCmd errStruct["Command"] = currentCmd
errStruct["Cmd"] = cmd errStruct["Cmd"] = cmd
errStruct["Args"] = config.Cmds[cmd].Args errStruct["Args"] = opts.Cmds[cmd].Args
errStruct["Err"] = runOutErr errStruct["Err"] = runOutErr
errStruct["CmdsRan"] = cmdsRan errStruct["CmdsRan"] = cmdsRan
errStruct["Output"] = outputArr errStruct["Output"] = outputArr
@ -390,17 +391,17 @@ func cmdListWorker(msgTemps *msgTemplates, jobs <-chan *CmdList, config *ConfigF
tmpErr := msgTemps.err.Execute(&errMsg, errStruct) tmpErr := msgTemps.err.Execute(&errMsg, errStruct)
if tmpErr != nil { if tmpErr != nil {
config.Logger.Err(tmpErr).Send() opts.Logger.Err(tmpErr).Send()
} }
notifySendErr := list.NotifyConfig.Send(context.Background(), fmt.Sprintf("List %s failed on command %s ", list.Name, cmd), errMsg.String()) notifySendErr := list.NotifyConfig.Send(context.Background(), fmt.Sprintf("List %s failed", list.Name), errMsg.String())
if notifySendErr != nil { if notifySendErr != nil {
config.Logger.Err(notifySendErr).Send() opts.Logger.Err(notifySendErr).Send()
} }
} }
config.Logger.Err(runOutErr).Send() opts.Logger.Err(runOutErr).Send()
break break
} else { } else {
@ -420,14 +421,14 @@ func cmdListWorker(msgTemps *msgTemplates, jobs <-chan *CmdList, config *ConfigF
tmpErr := msgTemps.success.Execute(&successMsg, successStruct) tmpErr := msgTemps.success.Execute(&successMsg, successStruct)
if tmpErr != nil { if tmpErr != nil {
config.Logger.Err(tmpErr).Send() opts.Logger.Err(tmpErr).Send()
break break
} }
err := list.NotifyConfig.Send(context.Background(), fmt.Sprintf("List %s succeded", list.Name), successMsg.String()) err := list.NotifyConfig.Send(context.Background(), fmt.Sprintf("List %s succeded", list.Name), successMsg.String())
if err != nil { if err != nil {
config.Logger.Err(err).Send() opts.Logger.Err(err).Send()
} }
} }
} else { } else {
@ -442,22 +443,22 @@ func cmdListWorker(msgTemps *msgTemplates, jobs <-chan *CmdList, config *ConfigF
} }
// RunListConfig runs a command list from the ConfigFile. // RunListConfig runs a command list from the ConfigFile.
func (config *ConfigFile) RunListConfig(cron string, opts *ConfigOpts) { func (opts *ConfigOpts) RunListConfig(cron string) {
mTemps := &msgTemplates{ mTemps := &msgTemplates{
err: template.Must(template.New("error.txt").ParseFS(templates, "templates/error.txt")), err: template.Must(template.New("error.txt").ParseFS(templates, "templates/error.txt")),
success: template.Must(template.New("success.txt").ParseFS(templates, "templates/success.txt")), success: template.Must(template.New("success.txt").ParseFS(templates, "templates/success.txt")),
} }
configListsLen := len(config.CmdConfigLists) configListsLen := len(opts.CmdConfigLists)
listChan := make(chan *CmdList, configListsLen) listChan := make(chan *CmdList, configListsLen)
results := make(chan string) results := make(chan string)
// This starts up list workers, initially blocked // This starts up list workers, initially blocked
// because there are no jobs yet. // because there are no jobs yet.
for w := 1; w <= configListsLen; w++ { for w := 1; w <= configListsLen; w++ {
go cmdListWorker(mTemps, listChan, config, results, opts) go cmdListWorker(mTemps, listChan, results, opts)
} }
for listName, cmdConfig := range config.CmdConfigLists { for listName, cmdConfig := range opts.CmdConfigLists {
if cmdConfig.Name == "" { if cmdConfig.Name == "" {
cmdConfig.Name = listName cmdConfig.Name = listName
} }
@ -475,26 +476,26 @@ func (config *ConfigFile) RunListConfig(cron string, opts *ConfigOpts) {
<-results <-results
} }
config.closeHostConnections() opts.closeHostConnections()
} }
func (config *ConfigFile) ExecuteCmds(opts *ConfigOpts) { func (config *ConfigOpts) ExecuteCmds(opts *ConfigOpts) {
for _, cmd := range opts.executeCmds { for _, cmd := range opts.executeCmds {
cmdToRun := config.Cmds[cmd] cmdToRun := opts.Cmds[cmd]
cmdLogger := config.Logger.With(). cmdLogger := opts.Logger.With().
Str("backy-cmd", cmd). Str("backy-cmd", cmd).
Logger() Logger()
_, runErr := cmdToRun.RunCmd(cmdLogger, config, opts) _, runErr := cmdToRun.RunCmd(cmdLogger, opts)
if runErr != nil { if runErr != nil {
config.Logger.Err(runErr).Send() opts.Logger.Err(runErr).Send()
} }
} }
config.closeHostConnections() opts.closeHostConnections()
} }
func (c *ConfigFile) closeHostConnections() { func (c *ConfigOpts) closeHostConnections() {
for _, host := range c.Hosts { for _, host := range c.Hosts {
c.Logger.Info().Str("server", host.HostName) c.Logger.Info().Str("server", host.HostName)
if host.isProxyHost { if host.isProxyHost {

@ -10,40 +10,58 @@ import (
"git.andrewnw.xyz/CyberShell/backy/pkg/logging" "git.andrewnw.xyz/CyberShell/backy/pkg/logging"
vault "github.com/hashicorp/vault/api" vault "github.com/hashicorp/vault/api"
"github.com/knadh/koanf/parsers/yaml"
"github.com/knadh/koanf/providers/file"
"github.com/knadh/koanf/v2"
"github.com/mattn/go-isatty" "github.com/mattn/go-isatty"
"github.com/rs/zerolog" "github.com/rs/zerolog"
"github.com/spf13/viper"
) )
var homeDir string
var homeDirErr error
var backyHomeConfDir string
var configFiles []string
func (opts *ConfigOpts) InitConfig() { func (opts *ConfigOpts) InitConfig() {
if opts.viper != nil {
return
}
backyViper := viper.New()
if strings.TrimSpace(opts.ConfigFilePath) != "" { homeDir, homeDirErr = os.UserHomeDir()
if homeDirErr != nil {
fmt.Println(homeDirErr)
}
backyHomeConfDir = homeDir + "/.config/backy/"
configFiles = []string{"./backy.yml", "./backy.yaml", backyHomeConfDir + "backy.yml", backyHomeConfDir + "backy.yaml"}
backyKoanf := koanf.New(".")
opts.ConfigFilePath = strings.TrimSpace(opts.ConfigFilePath)
if opts.ConfigFilePath != "" {
err := testFile(opts.ConfigFilePath) err := testFile(opts.ConfigFilePath)
if err != nil { if err != nil {
logging.ExitWithMSG(fmt.Sprintf("Could not open config file %s: %v", opts.ConfigFilePath, err), 1, nil) logging.ExitWithMSG(fmt.Sprintf("Could not open config file %s: %v", opts.ConfigFilePath, err), 1, nil)
} }
backyViper.SetConfigFile(opts.ConfigFilePath)
if err := backyKoanf.Load(file.Provider(opts.ConfigFilePath), yaml.Parser()); err != nil {
logging.ExitWithMSG(fmt.Sprintf("error loading config: %v", err), 1, &opts.Logger)
}
} else { } else {
backyViper.SetConfigName("backy.yml") // name of config file (with extension)
backyViper.SetConfigName("backy.yaml") // name of config file (with extension) cFileFalures := 0
backyViper.SetConfigType("yaml") // REQUIRED if the config file does not have the extension in the name for _, c := range configFiles {
backyViper.AddConfigPath(".") // optionally look for config in the working directory if err := backyKoanf.Load(file.Provider(c), yaml.Parser()); err != nil {
backyViper.AddConfigPath("$HOME/.config/backy") // call multiple times to add many search paths cFileFalures++
} } else {
err := backyViper.ReadInConfig() // Find and read the config file opts.ConfigFilePath = c
if err != nil { // Handle errors reading the config file break
msg := fmt.Sprintf("fatal error reading config file %s: %v", backyViper.ConfigFileUsed(), err) }
logging.ExitWithMSG(msg, 1, nil) }
} if cFileFalures == len(configFiles) {
opts.viper = backyViper logging.ExitWithMSG(fmt.Sprintf("could not find a config file. Put one in the following paths: %v", configFiles), 1, &opts.Logger)
}
}
opts.koanf = backyKoanf
} }
// ReadConfig validates and reads the config file. // ReadConfig validates and reads the config file.
func ReadConfig(opts *ConfigOpts) *ConfigFile { func ReadConfig(opts *ConfigOpts) *ConfigOpts {
if isatty.IsTerminal(os.Stdout.Fd()) { if isatty.IsTerminal(os.Stdout.Fd()) {
os.Setenv("BACKY_TERM", "enabled") os.Setenv("BACKY_TERM", "enabled")
@ -53,44 +71,38 @@ func ReadConfig(opts *ConfigOpts) *ConfigFile {
os.Setenv("BACKY_TERM", "disabled") os.Setenv("BACKY_TERM", "disabled")
} }
backyConfigFile := NewConfig() backyKoanf := opts.koanf
backyViper := opts.viper
opts.loadEnv()
// envFileInConfigDir := fmt.Sprintf("%s/.env", path.Dir(backyViper.ConfigFileUsed()))
// load the .env file in config file directory opts.loadEnv()
// _ = godotenv.Load(envFileInConfigDir)
if backyViper.GetBool(getNestedConfig("logging", "cmd-std-out")) { if backyKoanf.Bool(getNestedConfig("logging", "cmd-std-out")) {
os.Setenv("BACKY_STDOUT", "enabled") os.Setenv("BACKY_STDOUT", "enabled")
} }
CheckConfigValues(backyViper) CheckConfigValues(backyKoanf, opts.ConfigFilePath)
for _, c := range opts.executeCmds { for _, c := range opts.executeCmds {
if !backyViper.IsSet(getCmdFromConfig(c)) { if !backyKoanf.Exists(getCmdFromConfig(c)) {
logging.ExitWithMSG(Sprintf("command %s is not in config file %s", c, backyViper.ConfigFileUsed()), 1, nil) logging.ExitWithMSG(Sprintf("command %s is not in config file %s", c, opts.ConfigFilePath), 1, nil)
} }
} }
for _, l := range opts.executeLists { for _, l := range opts.executeLists {
if !backyViper.IsSet(getCmdListFromConfig(l)) { if !backyKoanf.Exists(getCmdListFromConfig(l)) {
logging.ExitWithMSG(Sprintf("list %s not found", l), 1, nil) logging.ExitWithMSG(Sprintf("list %s not found", l), 1, nil)
} }
} }
var ( var (
// backyLoggingOpts *viper.Viper
verbose bool verbose bool
logFile string logFile string
) )
verbose = backyViper.GetBool(getLoggingKeyFromConfig("verbose")) verbose = backyKoanf.Bool(getLoggingKeyFromConfig("verbose"))
logFile = fmt.Sprintf("%s/backy.log", path.Dir(backyViper.ConfigFileUsed())) logFile = fmt.Sprintf("%s/backy.log", path.Dir(opts.ConfigFilePath))
if backyViper.IsSet(getLoggingKeyFromConfig("file")) { if backyKoanf.Exists(getLoggingKeyFromConfig("file")) {
logFile = backyViper.GetString(getLoggingKeyFromConfig("file")) logFile = backyKoanf.String(getLoggingKeyFromConfig("file"))
} }
zerolog.SetGlobalLevel(zerolog.InfoLevel) zerolog.SetGlobalLevel(zerolog.InfoLevel)
if verbose { if verbose {
@ -99,7 +111,7 @@ func ReadConfig(opts *ConfigOpts) *ConfigFile {
os.Setenv("BACKY_LOGLEVEL", Sprintf("%v", globalLvl)) os.Setenv("BACKY_LOGLEVEL", Sprintf("%v", globalLvl))
} }
consoleLoggingDisabled := backyViper.GetBool(getLoggingKeyFromConfig("console-disabled")) consoleLoggingDisabled := backyKoanf.Bool(getLoggingKeyFromConfig("console-disabled"))
os.Setenv("BACKY_CONSOLE_LOGGING", "enabled") os.Setenv("BACKY_CONSOLE_LOGGING", "enabled")
// Other qualifiers can go here as well // Other qualifiers can go here as well
@ -111,42 +123,30 @@ func ReadConfig(opts *ConfigOpts) *ConfigFile {
log := zerolog.New(writers).With().Timestamp().Logger() log := zerolog.New(writers).With().Timestamp().Logger()
backyConfigFile.Logger = log opts.Logger = log
log.Info().Str("config file", opts.ConfigFilePath).Send()
log.Info().Str("config file", backyViper.ConfigFileUsed()).Send() unmarshalErr := backyKoanf.UnmarshalWithConf("commands", &opts.Cmds, koanf.UnmarshalConf{Tag: "yaml"})
commandsMap := backyViper.GetStringMapString("commands")
commandsMapViper := backyViper.Sub("commands")
unmarshalErr := commandsMapViper.Unmarshal(&backyConfigFile.Cmds)
if unmarshalErr != nil { if unmarshalErr != nil {
panic(fmt.Errorf("error unmarshalling cmds struct: %w", unmarshalErr)) panic(fmt.Errorf("error unmarshalling cmds struct: %w", unmarshalErr))
} }
hostConfigsMap := make(map[string]*viper.Viper) for cmdName, cmdConf := range opts.Cmds {
for cmdName, cmdConf := range backyConfigFile.Cmds {
envFileErr := testFile(cmdConf.Env) envFileErr := testFile(cmdConf.Env)
if envFileErr != nil { if envFileErr != nil {
backyConfigFile.Logger.Info().Str("cmd", cmdName).Err(envFileErr).Send() opts.Logger.Info().Str("cmd", cmdName).Err(envFileErr).Send()
os.Exit(1) os.Exit(1)
} }
expandEnvVars(opts.backyEnv, cmdConf.Environment) expandEnvVars(opts.backyEnv, cmdConf.Environment)
host := cmdConf.Host
if host != nil {
if backyViper.IsSet(getNestedConfig("hosts", *host)) {
hostconfig := backyViper.Sub(getNestedConfig("hosts", *host))
hostConfigsMap[*host] = hostconfig
}
}
} }
hostsMapViper := backyViper.Sub("hosts") unmarshalErr = backyKoanf.UnmarshalWithConf("hosts", &opts.Hosts, koanf.UnmarshalConf{Tag: "yaml"})
unmarshalErr = hostsMapViper.Unmarshal(&backyConfigFile.Hosts)
if unmarshalErr != nil { if unmarshalErr != nil {
panic(fmt.Errorf("error unmarshalling hosts struct: %w", unmarshalErr)) panic(fmt.Errorf("error unmarshalling hosts struct: %w", unmarshalErr))
} }
for hostConfigName, host := range backyConfigFile.Hosts { for hostConfigName, host := range opts.Hosts {
if host.Host == "" { if host.Host == "" {
host.Host = hostConfigName host.Host = hostConfigName
} }
@ -155,7 +155,7 @@ func ReadConfig(opts *ConfigOpts) *ConfigFile {
if len(proxyHosts) > 1 { if len(proxyHosts) > 1 {
for hostNum, h := range proxyHosts { for hostNum, h := range proxyHosts {
if hostNum > 1 { if hostNum > 1 {
proxyHost, defined := backyConfigFile.Hosts[h] proxyHost, defined := opts.Hosts[h]
if defined { if defined {
host.ProxyHost = append(host.ProxyHost, proxyHost) host.ProxyHost = append(host.ProxyHost, proxyHost)
} else { } else {
@ -163,7 +163,7 @@ func ReadConfig(opts *ConfigOpts) *ConfigFile {
host.ProxyHost = append(host.ProxyHost, newProxy) host.ProxyHost = append(host.ProxyHost, newProxy)
} }
} else { } else {
proxyHost, defined := backyConfigFile.Hosts[h] proxyHost, defined := opts.Hosts[h]
if defined { if defined {
host.ProxyHost = append(host.ProxyHost, proxyHost) host.ProxyHost = append(host.ProxyHost, proxyHost)
} else { } else {
@ -173,7 +173,7 @@ func ReadConfig(opts *ConfigOpts) *ConfigFile {
} }
} }
} else { } else {
proxyHost, defined := backyConfigFile.Hosts[proxyHosts[0]] proxyHost, defined := opts.Hosts[proxyHosts[0]]
if defined { if defined {
host.ProxyHost = append(host.ProxyHost, proxyHost) host.ProxyHost = append(host.ProxyHost, proxyHost)
} else { } else {
@ -184,33 +184,29 @@ func ReadConfig(opts *ConfigOpts) *ConfigFile {
} }
} }
cmdListCfg := backyViper.Sub("cmd-configs") if backyKoanf.Exists("cmd-lists") {
unmarshalErr = cmdListCfg.Unmarshal(&backyConfigFile.CmdConfigLists) unmarshalErr = backyKoanf.UnmarshalWithConf("cmd-lists", &opts.CmdConfigLists, koanf.UnmarshalConf{Tag: "yaml"})
if unmarshalErr != nil { if unmarshalErr != nil {
panic(fmt.Errorf("error unmarshalling cmd list struct: %w", unmarshalErr)) logging.ExitWithMSG((fmt.Sprintf("error unmarshalling cmd list struct: %v", unmarshalErr)), 1, &opts.Logger)
}
} }
var cmdNotFoundSliceErr []error var cmdNotFoundSliceErr []error
for cmdListName, cmdList := range backyConfigFile.CmdConfigLists { for cmdListName, cmdList := range opts.CmdConfigLists {
if opts.useCron { if opts.useCron {
cron := strings.TrimSpace(cmdList.Cron) cron := strings.TrimSpace(cmdList.Cron)
if cron == "" { if cron == "" {
delete(backyConfigFile.CmdConfigLists, cmdListName) delete(opts.CmdConfigLists, cmdListName)
} }
} }
for _, cmdInList := range cmdList.Order { for _, cmdInList := range cmdList.Order {
_, cmdNameFound := backyConfigFile.Cmds[cmdInList] _, cmdNameFound := opts.Cmds[cmdInList]
if !cmdNameFound { if !cmdNameFound {
cmdNotFoundStr := fmt.Sprintf("command %s in list %s is not defined in commands section in config file", cmdInList, cmdListName) cmdNotFoundStr := fmt.Sprintf("command %s in list %s is not defined in commands section in config file", cmdInList, cmdListName)
cmdNotFoundErr := errors.New(cmdNotFoundStr) cmdNotFoundErr := errors.New(cmdNotFoundStr)
cmdNotFoundSliceErr = append(cmdNotFoundSliceErr, cmdNotFoundErr) cmdNotFoundSliceErr = append(cmdNotFoundSliceErr, cmdNotFoundErr)
} }
} }
for _, notificationID := range cmdList.Notifications {
if !backyViper.IsSet(getNestedConfig("notifications", notificationID)) {
logging.ExitWithMSG(fmt.Sprintf("%s in list %s not found in notifications", notificationID, cmdListName), 1, nil)
}
}
} }
if len(cmdNotFoundSliceErr) > 0 { if len(cmdNotFoundSliceErr) > 0 {
@ -218,39 +214,35 @@ func ReadConfig(opts *ConfigOpts) *ConfigFile {
cmdNotFoundErrorLog.Errs("commands not found", cmdNotFoundSliceErr).Send() cmdNotFoundErrorLog.Errs("commands not found", cmdNotFoundSliceErr).Send()
} }
if opts.useCron && (len(backyConfigFile.CmdConfigLists) == 0) { if opts.useCron && (len(opts.CmdConfigLists) == 0) {
logging.ExitWithMSG("No cron fields detected in any command lists", 1, nil) logging.ExitWithMSG("No cron fields detected in any command lists", 1, nil)
} }
for c := range commandsMap { for c := range opts.Cmds {
if opts.executeCmds != nil && !contains(opts.executeCmds, c) { if opts.executeCmds != nil && !contains(opts.executeCmds, c) {
delete(backyConfigFile.Cmds, c) delete(opts.Cmds, c)
} }
} }
if len(opts.executeLists) > 0 { if len(opts.executeLists) > 0 {
for l := range backyConfigFile.CmdConfigLists { for l := range opts.CmdConfigLists {
if !contains(opts.executeLists, l) { if !contains(opts.executeLists, l) {
delete(backyConfigFile.CmdConfigLists, l) delete(opts.CmdConfigLists, l)
} }
} }
} }
if backyViper.IsSet("notifications") { if backyKoanf.Exists("notifications") {
notificationsMap := backyViper.GetStringMap("notifications")
for id := range notificationsMap { unmarshalErr = backyKoanf.UnmarshalWithConf("notifications", &opts.NotificationConf, koanf.UnmarshalConf{Tag: "yaml"})
notifConfig := backyViper.Sub(getNestedConfig("notifications", id)) if unmarshalErr != nil {
config := &NotificationsConfig{ fmt.Printf("error unmarshalling notifications object: %v", unmarshalErr)
Config: notifConfig,
Enabled: true,
}
backyConfigFile.Notifications[id] = config
} }
} }
for _, cmd := range backyConfigFile.Cmds { for _, cmd := range opts.Cmds {
if cmd.Host != nil { if cmd.Host != nil {
host, hostFound := backyConfigFile.Hosts[*cmd.Host] host, hostFound := opts.Hosts[*cmd.Host]
if hostFound { if hostFound {
cmd.RemoteHost = host cmd.RemoteHost = host
cmd.RemoteHost.Host = host.Host cmd.RemoteHost.Host = host.Host
@ -258,19 +250,18 @@ func ReadConfig(opts *ConfigOpts) *ConfigFile {
cmd.RemoteHost.HostName = host.HostName cmd.RemoteHost.HostName = host.HostName
} }
} else { } else {
backyConfigFile.Hosts[*cmd.Host] = &Host{Host: *cmd.Host} opts.Hosts[*cmd.Host] = &Host{Host: *cmd.Host}
cmd.RemoteHost = &Host{Host: *cmd.Host} cmd.RemoteHost = &Host{Host: *cmd.Host}
} }
} }
} }
backyConfigFile.SetupNotify() opts.SetupNotify()
opts.ConfigFile = backyConfigFile
if err := opts.setupVault(); err != nil { if err := opts.setupVault(); err != nil {
log.Err(err).Send() log.Err(err).Send()
} }
opts.ConfigFile = backyConfigFile
return backyConfigFile return opts
} }
func getNestedConfig(nestedConfig, key string) string { func getNestedConfig(nestedConfig, key string) string {
@ -289,16 +280,16 @@ func getLoggingKeyFromConfig(key string) string {
} }
func getCmdListFromConfig(list string) string { func getCmdListFromConfig(list string) string {
return fmt.Sprintf("cmd-configs.%s", list) return fmt.Sprintf("cmd-lists.%s", list)
} }
func (opts *ConfigOpts) setupVault() error { func (opts *ConfigOpts) setupVault() error {
if !opts.viper.GetBool("vault.enabled") { if !opts.koanf.Bool("vault.enabled") {
return nil return nil
} }
config := vault.DefaultConfig() config := vault.DefaultConfig()
config.Address = opts.viper.GetString("vault.address") config.Address = opts.koanf.String("vault.address")
if strings.TrimSpace(config.Address) == "" { if strings.TrimSpace(config.Address) == "" {
config.Address = os.Getenv("VAULT_ADDR") config.Address = os.Getenv("VAULT_ADDR")
} }
@ -308,7 +299,7 @@ func (opts *ConfigOpts) setupVault() error {
return err return err
} }
token := opts.viper.GetString("vault.token") token := opts.koanf.String("vault.token")
if strings.TrimSpace(token) == "" { if strings.TrimSpace(token) == "" {
token = os.Getenv("VAULT_TOKEN") token = os.Getenv("VAULT_TOKEN")
} }
@ -318,10 +309,9 @@ func (opts *ConfigOpts) setupVault() error {
client.SetToken(token) client.SetToken(token)
cmdListCfg := opts.viper.Sub("viper.keys") unmarshalErr := opts.koanf.UnmarshalWithConf("vault.keys", &opts.VaultKeys, koanf.UnmarshalConf{Tag: "yaml"})
unmarshalErr := cmdListCfg.Unmarshal(&opts.VaultKeys)
if unmarshalErr != nil { if unmarshalErr != nil {
panic(fmt.Errorf("error unmarshalling viper.keys into struct: %w", unmarshalErr)) logging.ExitWithMSG(fmt.Sprintf("error unmarshalling vault.keys into struct: %v", unmarshalErr), 1, &opts.Logger)
} }
opts.vaultClient = client opts.vaultClient = client

@ -5,31 +5,34 @@
package backy package backy
import ( import (
"fmt"
"strings" "strings"
"time" "time"
"git.andrewnw.xyz/CyberShell/backy/pkg/logging"
"github.com/go-co-op/gocron" "github.com/go-co-op/gocron"
) )
func (opts *ConfigOpts) Cron() { func (opts *ConfigOpts) Cron() {
s := gocron.NewScheduler(time.Local) s := gocron.NewScheduler(time.Local)
s.TagsUnique() s.TagsUnique()
cmdLists := opts.ConfigFile.CmdConfigLists cmdLists := opts.CmdConfigLists
for listName, config := range cmdLists { for listName, config := range cmdLists {
if config.Name == "" { if config.Name == "" {
config.Name = listName config.Name = listName
} }
cron := strings.TrimSpace(config.Cron) cron := strings.TrimSpace(config.Cron)
if cron != "" { if cron != "" {
opts.ConfigFile.Logger.Info().Str("Scheduling cron list", config.Name).Str("Time", cron).Send() opts.Logger.Info().Str("Scheduling cron list", config.Name).Str("Time", cron).Send()
_, err := s.CronWithSeconds(cron).Tag(config.Name).Do(func(cron string) { _, err := s.CronWithSeconds(cron).Tag(config.Name).Do(func(cron string) {
opts.ConfigFile.RunListConfig(cron, opts) opts.RunListConfig(cron)
}, cron) }, cron)
if err != nil { if err != nil {
panic(err) logging.ExitWithMSG(fmt.Sprintf("error: %v", err), 1, &opts.Logger)
} }
} }
} }
opts.ConfigFile.Logger.Info().Msg("Starting cron mode...") opts.Logger.Info().Msg("Starting cron mode...")
s.StartBlocking() s.StartBlocking()
} }

@ -0,0 +1,5 @@
package backy
func (opts *ConfigOpts) ListConfiguration() {
}

@ -18,7 +18,7 @@ const mongoConfigKey = "global.mongo"
func (opts *ConfigOpts) InitMongo() { func (opts *ConfigOpts) InitMongo() {
if !opts.viper.GetBool(getMongoConfigKey("enabled")) { if !opts.koanf.Bool(getMongoConfigKey("enabled")) {
return return
} }
var ( var (
@ -27,37 +27,37 @@ func (opts *ConfigOpts) InitMongo() {
) )
// TODO: Get uri and creditials from config // TODO: Get uri and creditials from config
host := opts.viper.GetString(getMongoConfigKey("host")) host := opts.koanf.String(getMongoConfigKey("host"))
port := opts.viper.GetInt32(getMongoConfigKey("port")) port := opts.koanf.Int64(getMongoConfigKey("port"))
ctx, ctxCancel := context.WithTimeout(context.Background(), 10*time.Second) ctx, ctxCancel := context.WithTimeout(context.Background(), 10*time.Second)
defer ctxCancel() defer ctxCancel()
client, err = mongo.Connect(ctx, options.Client().ApplyURI(fmt.Sprintf("mongo://%s:%d", host, port))) client, err = mongo.Connect(ctx, options.Client().ApplyURI(fmt.Sprintf("mongo://%s:%d", host, port)))
if opts.viper.GetBool(getMongoConfigKey("prod")) { if opts.koanf.Bool(getMongoConfigKey("prod")) {
mongoEnvFileSet := opts.viper.IsSet(getMongoConfigKey("env")) mongoEnvFileSet := opts.koanf.Exists(getMongoConfigKey("env"))
if mongoEnvFileSet { if mongoEnvFileSet {
getMongoConfigFromEnv(opts) getMongoConfigFromEnv(opts)
} }
auth := options.Credential{} auth := options.Credential{}
auth.Password = opts.viper.GetString("global.mongo.password") auth.Password = opts.koanf.String("global.mongo.password")
auth.Username = opts.viper.GetString("global.mongo.username") auth.Username = opts.koanf.String("global.mongo.username")
client, err = mongo.Connect(ctx, options.Client().SetAuth(auth).ApplyURI("mongodb://localhost:27017")) client, err = mongo.Connect(ctx, options.Client().SetAuth(auth).ApplyURI("mongodb://localhost:27017"))
} }
if err != nil { if err != nil {
opts.ConfigFile.Logger.Fatal().Err(err).Send() opts.Logger.Fatal().Err(err).Send()
} }
if err != nil { if err != nil {
opts.ConfigFile.Logger.Fatal().Err(err).Send() opts.Logger.Fatal().Err(err).Send()
} }
defer client.Disconnect(ctx) defer client.Disconnect(ctx)
err = client.Ping(ctx, readpref.Primary()) err = client.Ping(ctx, readpref.Primary())
if err != nil { if err != nil {
opts.ConfigFile.Logger.Fatal().Err(err).Send() opts.Logger.Fatal().Err(err).Send()
} }
databases, err := client.ListDatabaseNames(ctx, bson.M{}) databases, err := client.ListDatabaseNames(ctx, bson.M{})
if err != nil { if err != nil {
opts.ConfigFile.Logger.Fatal().Err(err).Send() opts.Logger.Fatal().Err(err).Send()
} }
fmt.Println(databases) fmt.Println(databases)
backyDB := client.Database("backy") backyDB := client.Database("backy")
@ -68,7 +68,7 @@ func (opts *ConfigOpts) InitMongo() {
} }
func getMongoConfigFromEnv(opts *ConfigOpts) error { func getMongoConfigFromEnv(opts *ConfigOpts) error {
mongoEnvFile, err := os.Open(opts.viper.GetString("global.mongo.env")) mongoEnvFile, err := os.Open(opts.koanf.String("global.mongo.env"))
if err != nil { if err != nil {
return err return err
} }
@ -84,8 +84,8 @@ func getMongoConfigFromEnv(opts *ConfigOpts) error {
if !mongoUserFound { if !mongoUserFound {
return errors.New("MONGO_PASSWORD not set in " + mongoEnvFile.Name()) return errors.New("MONGO_PASSWORD not set in " + mongoEnvFile.Name())
} }
opts.viper.Set(mongoConfigKey+".password", mongoPW) opts.koanf.Set(mongoConfigKey+".password", mongoPW)
opts.viper.Set(mongoConfigKey+".username", mongoUser) opts.koanf.Set(mongoConfigKey+".username", mongoUser)
return nil return nil
} }

@ -5,67 +5,76 @@ package backy
import ( import (
"fmt" "fmt"
"strings"
"git.andrewnw.xyz/CyberShell/backy/pkg/logging"
"github.com/nikoksr/notify" "github.com/nikoksr/notify"
"github.com/nikoksr/notify/service/mail" "github.com/nikoksr/notify/service/mail"
"github.com/nikoksr/notify/service/matrix" "github.com/nikoksr/notify/service/matrix"
"maunium.net/go/mautrix/id" "maunium.net/go/mautrix/id"
) )
type matrixStruct struct { type MatrixStruct struct {
homeserver string Homeserver string `yaml:"homeserver"`
roomid id.RoomID Roomid id.RoomID `yaml:"room-id"`
accessToken string AccessToken string `yaml:"access-token"`
userId id.UserID UserId id.UserID `yaml:"user-id"`
} }
type mailConfig struct { type MailConfig struct {
senderaddress string Host string `yaml:"host"`
host string Port string `yaml:"port"`
to []string Username string `yaml:"username"`
username string SenderAddress string `yaml:"senderaddress"`
password string To []string `yaml:"to"`
port string Password string `yaml:"password"`
}
func SetupCommandsNotifiers(backyConfig ConfigFile, ids ...string) {
} }
// SetupNotify sets up notify instances for each command list. // SetupNotify sets up notify instances for each command list.
func (opts *ConfigOpts) SetupNotify() {
func (backyConfig *ConfigFile) SetupNotify() { // check if we have individual commands instead of lists to execute
if len(opts.executeCmds) != 0 {
return
}
for _, cmdConfig := range backyConfig.CmdConfigLists { for confName, cmdConfig := range opts.CmdConfigLists {
var services []notify.Notifier var services []notify.Notifier
for notifyID := range backyConfig.Notifications { for _, id := range cmdConfig.Notifications {
if contains(cmdConfig.Notifications, notifyID) { if !strings.Contains(id, ".") {
opts.Logger.Info().Str("id", id).Str("list", cmdConfig.Name).Msg("key does not contain a \".\" Make sure to follow the docs: https://backy.cybershell.xyz/config/notifications/")
logging.ExitWithMSG(fmt.Sprintf("notification id %s in cmd list %s does not contain a \".\" \nMake sure to follow the docs: https://backy.cybershell.xyz/config/notifications/", id, cmdConfig.Name), 1, &opts.Logger)
}
if backyConfig.Notifications[notifyID].Enabled { confSplit := strings.Split(id, ".")
config := backyConfig.Notifications[notifyID].Config confType := confSplit[0]
switch config.GetString("type") { confId := confSplit[1]
case "matrix": switch confType {
mtrx := matrixStruct{
userId: id.UserID(config.GetString("user-id")), case "mail":
roomid: id.RoomID(config.GetString("room-id")), conf, ok := opts.NotificationConf.MailConfig[confId]
accessToken: config.GetString("access-token"), if !ok {
homeserver: config.GetString("homeserver"), opts.Logger.Info().Err(fmt.Errorf("error: ID %s not found in mail object", confId)).Str("list", confName).Send()
} continue
mtrxClient, _ := setupMatrix(mtrx) }
services = append(services, mtrxClient) mailConf := setupMail(conf)
case "mail": services = append(services, mailConf)
mailCfg := mailConfig{ case "matrix":
senderaddress: config.GetString("senderaddress"), conf, ok := opts.NotificationConf.MatrixConfig[confId]
password: config.GetString("password"), if !ok {
username: config.GetString("username"), opts.Logger.Info().Err(fmt.Errorf("error: ID %s not found in matrix object", confId)).Str("list", confName).Send()
to: config.GetStringSlice("to"), continue
host: config.GetString("host"),
port: fmt.Sprint(config.GetUint16("port")),
}
mailClient := setupMail(mailCfg)
services = append(services, mailClient)
}
} }
mtrxConf, mtrxErr := setupMatrix(conf)
if mtrxErr != nil {
opts.Logger.Info().Str("list", confName).Err(fmt.Errorf("error: configuring matrix id %s failed during setup: %w", id, mtrxErr))
continue
}
// append the services
services = append(services, mtrxConf)
// service is not recognized
default:
opts.Logger.Info().Err(fmt.Errorf("id %s not found", id)).Str("list", confName).Send()
} }
} }
cmdConfig.NotifyConfig = notify.NewWithServices(services...) cmdConfig.NotifyConfig = notify.NewWithServices(services...)
@ -74,19 +83,18 @@ func (backyConfig *ConfigFile) SetupNotify() {
// logging.ExitWithMSG("This was a test of notifications", 0, nil) // logging.ExitWithMSG("This was a test of notifications", 0, nil)
} }
func setupMatrix(config matrixStruct) (*matrix.Matrix, error) { func setupMatrix(config MatrixStruct) (*matrix.Matrix, error) {
matrixClient, matrixErr := matrix.New(config.userId, config.roomid, config.homeserver, config.accessToken) matrixClient, matrixErr := matrix.New(config.UserId, config.Roomid, config.Homeserver, config.AccessToken)
if matrixErr != nil { if matrixErr != nil {
panic(matrixErr) return nil, matrixErr
} }
return matrixClient, nil return matrixClient, nil
} }
func setupMail(config mailConfig) *mail.Mail { func setupMail(config MailConfig) *mail.Mail {
mailClient := mail.New(config.senderaddress, config.host+":"+config.port) mailClient := mail.New(config.SenderAddress, config.Host+":"+config.Port)
mailClient.AuthenticateSMTP("", config.username, config.password, config.host) mailClient.AuthenticateSMTP("", config.Username, config.Password, config.Host)
mailClient.AddReceivers(config.to...) mailClient.AddReceivers(config.To...)
mailClient.BodyFormat(mail.PlainText) mailClient.BodyFormat(mail.PlainText)
return mailClient return mailClient
} }

@ -28,7 +28,7 @@ var TS = strings.TrimSpace
// It returns an ssh.Client used to run commands against. // It returns an ssh.Client used to run commands against.
// If configFile is empty, any required configuration is looked up in the default config files // If configFile is empty, any required configuration is looked up in the default config files
// If any value is not found, defaults are used // If any value is not found, defaults are used
func (remoteConfig *Host) ConnectToSSHHost(opts *ConfigOpts, config *ConfigFile) error { func (remoteConfig *Host) ConnectToSSHHost(opts *ConfigOpts) error {
// var sshClient *ssh.Client // var sshClient *ssh.Client
var connectErr error var connectErr error
@ -47,6 +47,11 @@ func (remoteConfig *Host) ConnectToSSHHost(opts *ConfigOpts, config *ConfigFile)
var configFile *os.File var configFile *os.File
var sshConfigFileOpenErr error var sshConfigFileOpenErr error
if !remoteConfig.useDefaultConfig { if !remoteConfig.useDefaultConfig {
var err error
remoteConfig.ConfigFilePath, err = resolveDir(remoteConfig.ConfigFilePath)
if err != nil {
return err
}
configFile, sshConfigFileOpenErr = os.Open(remoteConfig.ConfigFilePath) configFile, sshConfigFileOpenErr = os.Open(remoteConfig.ConfigFilePath)
if sshConfigFileOpenErr != nil { if sshConfigFileOpenErr != nil {
return sshConfigFileOpenErr return sshConfigFileOpenErr
@ -66,14 +71,14 @@ func (remoteConfig *Host) ConnectToSSHHost(opts *ConfigOpts, config *ConfigFile)
return decodeErr return decodeErr
} }
err := remoteConfig.GetProxyJumpFromConfig(config.Hosts) err := remoteConfig.GetProxyJumpFromConfig(opts.Hosts)
if err != nil { if err != nil {
return err return err
} }
if remoteConfig.ProxyHost != nil { if remoteConfig.ProxyHost != nil {
for _, proxyHost := range remoteConfig.ProxyHost { for _, proxyHost := range remoteConfig.ProxyHost {
err := proxyHost.GetProxyJumpConfig(config.Hosts, opts) err := proxyHost.GetProxyJumpConfig(opts.Hosts, opts)
opts.ConfigFile.Logger.Info().Msgf("Proxy host: %s", proxyHost.Host) opts.Logger.Info().Msgf("Proxy host: %s", proxyHost.Host)
if err != nil { if err != nil {
return err return err
} }
@ -101,24 +106,24 @@ func (remoteConfig *Host) ConnectToSSHHost(opts *ConfigOpts, config *ConfigFile)
return errors.Wrap(err, "could not create hostkeycallback function") return errors.Wrap(err, "could not create hostkeycallback function")
} }
remoteConfig.ClientConfig.HostKeyCallback = hostKeyCallback remoteConfig.ClientConfig.HostKeyCallback = hostKeyCallback
opts.ConfigFile.Logger.Info().Str("user", remoteConfig.ClientConfig.User).Send() opts.Logger.Info().Str("user", remoteConfig.ClientConfig.User).Send()
remoteConfig.SshClient, connectErr = remoteConfig.ConnectThroughBastion(opts.ConfigFile.Logger) remoteConfig.SshClient, connectErr = remoteConfig.ConnectThroughBastion(opts.Logger)
if connectErr != nil { if connectErr != nil {
return connectErr return connectErr
} }
if remoteConfig.SshClient != nil { if remoteConfig.SshClient != nil {
config.Hosts[remoteConfig.Host] = remoteConfig opts.Hosts[remoteConfig.Host] = remoteConfig
return nil return nil
} }
opts.ConfigFile.Logger.Info().Msgf("Connecting to host %s", remoteConfig.HostName) opts.Logger.Info().Msgf("Connecting to host %s", remoteConfig.HostName)
remoteConfig.SshClient, connectErr = ssh.Dial("tcp", remoteConfig.HostName, remoteConfig.ClientConfig) remoteConfig.SshClient, connectErr = ssh.Dial("tcp", remoteConfig.HostName, remoteConfig.ClientConfig)
if connectErr != nil { if connectErr != nil {
return connectErr return connectErr
} }
config.Hosts[remoteConfig.Host] = remoteConfig opts.Hosts[remoteConfig.Host] = remoteConfig
return nil return nil
} }
@ -148,7 +153,7 @@ func (remoteHost *Host) GetAuthMethods(opts *ConfigOpts) error {
if err != nil { if err != nil {
return err return err
} }
remoteHost.PrivateKeyPassword, err = GetPrivateKeyPassword(remoteHost.PrivateKeyPassword, opts, opts.ConfigFile.Logger) remoteHost.PrivateKeyPassword, err = GetPrivateKeyPassword(remoteHost.PrivateKeyPassword, opts, opts.Logger)
if err != nil { if err != nil {
return err return err
} }
@ -167,7 +172,7 @@ func (remoteHost *Host) GetAuthMethods(opts *ConfigOpts) error {
} }
} }
if remoteHost.Password == "" { if remoteHost.Password == "" {
remoteHost.Password, err = GetPassword(remoteHost.Password, opts, opts.ConfigFile.Logger) remoteHost.Password, err = GetPassword(remoteHost.Password, opts, opts.Logger)
if err != nil { if err != nil {
return err return err
} }
@ -297,7 +302,7 @@ func GetPrivateKeyPassword(key string, opts *ConfigOpts, log zerolog.Logger) (st
} else { } else {
prKeyPassword = key prKeyPassword = key
} }
prKeyPassword = GetVaultKey(prKeyPassword, opts, opts.ConfigFile.Logger) prKeyPassword = GetVaultKey(prKeyPassword, opts, opts.Logger)
return prKeyPassword, nil return prKeyPassword, nil
} }
@ -328,7 +333,7 @@ func GetPassword(pass string, opts *ConfigOpts, log zerolog.Logger) (string, err
} else { } else {
password = pass password = pass
} }
password = GetVaultKey(password, opts, opts.ConfigFile.Logger) password = GetVaultKey(password, opts, opts.Logger)
return password, nil return password, nil
} }

@ -6,9 +6,9 @@ import (
vaultapi "github.com/hashicorp/vault/api" vaultapi "github.com/hashicorp/vault/api"
"github.com/kevinburke/ssh_config" "github.com/kevinburke/ssh_config"
"github.com/knadh/koanf/v2"
"github.com/nikoksr/notify" "github.com/nikoksr/notify"
"github.com/rs/zerolog" "github.com/rs/zerolog"
"github.com/spf13/viper"
"go.mongodb.org/mongo-driver/bson/primitive" "go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo" "go.mongodb.org/mongo-driver/mongo"
"golang.org/x/crypto/ssh" "golang.org/x/crypto/ssh"
@ -37,7 +37,7 @@ type (
// Host defines a host to which to connect. // Host defines a host to which to connect.
// If not provided, the values will be looked up in the default ssh config files // If not provided, the values will be looked up in the default ssh config files
Host struct { Host struct {
ConfigFilePath string `yaml:"configfilepath,omitempty"` ConfigFilePath string `yaml:"config,omitempty"`
Host string `yaml:"host,omitempty"` Host string `yaml:"host,omitempty"`
HostName string `yaml:"hostname,omitempty"` HostName string `yaml:"hostname,omitempty"`
KnownHostsFile string `yaml:"knownhostsfile,omitempty"` KnownHostsFile string `yaml:"knownhostsfile,omitempty"`
@ -113,35 +113,26 @@ type (
Notifications []string `yaml:"notifications,omitempty"` Notifications []string `yaml:"notifications,omitempty"`
GetOutput bool `yaml:"getOutput,omitempty"` GetOutput bool `yaml:"getOutput,omitempty"`
NotifyConfig *notify.Notify NotifyConfig *notify.Notify
// NotificationsConfig map[string]*NotificationsConfig
// NotifyConfig map[string]*notify.Notify
} }
ConfigFile struct { ConfigOpts struct {
// Cmds holds the commands for a list. // Cmds holds the commands for a list.
// Key is the name of the command, // Key is the name of the command,
Cmds map[string]*Command `yaml:"commands"` Cmds map[string]*Command `yaml:"commands"`
// CmdConfigLists holds the lists of commands to be run in order. // CmdConfigLists holds the lists of commands to be run in order.
// Key is the command list name. // Key is the command list name.
CmdConfigLists map[string]*CmdList `yaml:"cmd-configs"` CmdConfigLists map[string]*CmdList `yaml:"cmd-lists"`
// Hosts holds the Host config. // Hosts holds the Host config.
// key is the host. // key is the host.
Hosts map[string]*Host `yaml:"hosts"` Hosts map[string]*Host `yaml:"hosts"`
// Notifications holds the config for different notifications.
Notifications map[string]*NotificationsConfig
Logger zerolog.Logger Logger zerolog.Logger
}
ConfigOpts struct {
// Global log level // Global log level
BackyLogLvl *string BackyLogLvl *string
// Holds config file
ConfigFile *ConfigFile
// Holds config file // Holds config file
ConfigFilePath string ConfigFilePath string
@ -160,9 +151,13 @@ type (
vaultClient *vaultapi.Client vaultClient *vaultapi.Client
List ListConfig
VaultKeys []*VaultKey `yaml:"keys"` VaultKeys []*VaultKey `yaml:"keys"`
viper *viper.Viper koanf *koanf.Koanf
NotificationConf *Notifications `yaml:"notifications"`
} }
outStruct struct { outStruct struct {
@ -185,9 +180,9 @@ type (
Keys []*VaultKey `yaml:"keys"` Keys []*VaultKey `yaml:"keys"`
} }
NotificationsConfig struct { Notifications struct {
Config *viper.Viper MailConfig map[string]MailConfig `yaml:"mail,omitempty"`
Enabled bool MatrixConfig map[string]MatrixStruct `yaml:"matrix,omitempty"`
} }
CmdOutput struct { CmdOutput struct {
@ -204,4 +199,10 @@ type (
success *template.Template success *template.Template
err *template.Template err *template.Template
} }
ListConfig struct {
Lists []string
Commands []string
Hosts []string
}
) )

@ -15,12 +15,65 @@ import (
"git.andrewnw.xyz/CyberShell/backy/pkg/logging" "git.andrewnw.xyz/CyberShell/backy/pkg/logging"
"github.com/joho/godotenv" "github.com/joho/godotenv"
"github.com/knadh/koanf/v2"
"github.com/rs/zerolog" "github.com/rs/zerolog"
"github.com/spf13/viper"
"golang.org/x/crypto/ssh" "golang.org/x/crypto/ssh"
"mvdan.cc/sh/v3/shell" "mvdan.cc/sh/v3/shell"
) )
func (c *ConfigOpts) LogLvl(level string) BackyOptionFunc {
return func(bco *ConfigOpts) {
c.BackyLogLvl = &level
}
}
// AddCommands adds commands to ConfigOpts
func AddCommands(commands []string) BackyOptionFunc {
return func(bco *ConfigOpts) {
bco.executeCmds = append(bco.executeCmds, commands...)
}
}
// AddCommandLists adds lists to ConfigOpts
func AddCommandLists(lists []string) BackyOptionFunc {
return func(bco *ConfigOpts) {
bco.executeLists = append(bco.executeLists, lists...)
}
}
// AddPrintLists adds lists to print out
func SetListsToSearch(lists []string) BackyOptionFunc {
return func(bco *ConfigOpts) {
bco.List.Lists = append(bco.List.Lists, lists...)
}
}
// AddPrintLists adds lists to print out
func SetCmdsToSearch(cmds []string) BackyOptionFunc {
return func(bco *ConfigOpts) {
bco.List.Commands = append(bco.List.Commands, cmds...)
}
}
// UseCron enables the execution of command lists at specified times
func UseCron() BackyOptionFunc {
return func(bco *ConfigOpts) {
bco.useCron = true
}
}
func NewOpts(configFilePath string, opts ...BackyOptionFunc) *ConfigOpts {
b := &ConfigOpts{}
b.ConfigFilePath = configFilePath
for _, opt := range opts {
if opt != nil {
opt(b)
}
}
return b
}
func injectEnvIntoSSH(envVarsToInject environmentVars, process *ssh.Session, opts *ConfigOpts, log zerolog.Logger) { func injectEnvIntoSSH(envVarsToInject environmentVars, process *ssh.Session, opts *ConfigOpts, log zerolog.Logger) {
if envVarsToInject.file != "" { if envVarsToInject.file != "" {
envPath, envPathErr := resolveDir(envVarsToInject.file) envPath, envPathErr := resolveDir(envVarsToInject.file)
@ -94,12 +147,12 @@ func contains(s []string, e string) bool {
return false return false
} }
func CheckConfigValues(config *viper.Viper) { func CheckConfigValues(config *koanf.Koanf, file string) {
for _, key := range requiredKeys { for _, key := range requiredKeys {
isKeySet := config.IsSet(key) isKeySet := config.Exists(key)
if !isKeySet { if !isKeySet {
logging.ExitWithMSG(Sprintf("Config key %s is not defined in %s. Please make sure this value is set and has the appropriate keys set.", key, config.ConfigFileUsed()), 1, nil) logging.ExitWithMSG(Sprintf("Config key %s is not defined in %s. Please make sure this value is set and has the appropriate keys set.", key, file), 1, nil)
} }
} }
} }
@ -116,64 +169,6 @@ func testFile(c string) error {
return nil return nil
} }
func (c *ConfigOpts) LogLvl(level string) BackyOptionFunc {
return func(bco *ConfigOpts) {
c.BackyLogLvl = &level
}
}
// AddCommands adds commands to ConfigOpts
func AddCommands(commands []string) BackyOptionFunc {
return func(bco *ConfigOpts) {
bco.executeCmds = append(bco.executeCmds, commands...)
}
}
// AddCommandLists adds lists to ConfigOpts
func AddCommandLists(lists []string) BackyOptionFunc {
return func(bco *ConfigOpts) {
bco.executeLists = append(bco.executeLists, lists...)
}
}
// UseCron enables the execution of command lists at specified times
func UseCron() BackyOptionFunc {
return func(bco *ConfigOpts) {
bco.useCron = true
}
}
// UseCron enables the execution of command lists at specified times
func (c *ConfigOpts) AddViper(v *viper.Viper) BackyOptionFunc {
return func(bco *ConfigOpts) {
c.viper = v
}
}
func NewOpts(configFilePath string, opts ...BackyOptionFunc) *ConfigOpts {
b := &ConfigOpts{}
b.ConfigFilePath = configFilePath
for _, opt := range opts {
if opt != nil {
opt(b)
}
}
return b
}
/*
NewConfig initializes new config that holds information from the config file
*/
func NewConfig() *ConfigFile {
return &ConfigFile{
Cmds: make(map[string]*Command),
CmdConfigLists: make(map[string]*CmdList),
Hosts: make(map[string]*Host),
Notifications: make(map[string]*NotificationsConfig),
}
}
func IsTerminalActive() bool { func IsTerminalActive() bool {
return os.Getenv("BACKY_TERM") == "enabled" return os.Getenv("BACKY_TERM") == "enabled"
} }
@ -203,7 +198,7 @@ func resolveDir(path string) (string, error) {
} }
func (opts *ConfigOpts) loadEnv() { func (opts *ConfigOpts) loadEnv() {
envFileInConfigDir := fmt.Sprintf("%s/.env", path.Dir(opts.viper.ConfigFileUsed())) envFileInConfigDir := fmt.Sprintf("%s/.env", path.Dir(opts.ConfigFilePath))
var backyEnv map[string]string var backyEnv map[string]string
backyEnv, envFileErr := godotenv.Read(envFileInConfigDir) backyEnv, envFileErr := godotenv.Read(envFileInConfigDir)
if envFileErr != nil { if envFileErr != nil {

Loading…
Cancel
Save