From a4214b2b3f471bba9b6d8d796d8594d2e7ef020b Mon Sep 17 00:00:00 2001 From: Andrew Woodlee Date: Fri, 8 Sep 2023 23:42:13 -0500 Subject: [PATCH] v0.4.0 --- .changes/v0.4.0.md | 13 ++ .../database/mediaDb.json | 0 .frontmatter/database/taxonomyDb.json | 1 + .github/workflows/release.yml | 2 +- cmd/backup.go | 6 +- cmd/exec.go | 2 +- cmd/list.go | 39 ++++ cmd/root.go | 2 +- cmd/version.go | 5 +- docs/content/cli/_index.md | 20 +- docs/content/config/command-lists.md | 23 ++- docs/content/config/notifications.md | 38 ++-- docs/frontmatter.json => frontmatter.json | 10 +- getCommandHelp | 7 + go.mod | 41 ++-- go.sum | 54 +++++ pkg/backy/backy.go | 95 ++++----- pkg/backy/config.go | 192 +++++++++--------- pkg/backy/cron.go | 13 +- pkg/backy/list.go | 5 + pkg/backy/mongo.go | 28 +-- pkg/backy/notification.go | 112 +++++----- pkg/backy/ssh.go | 31 +-- pkg/backy/types.go | 37 ++-- pkg/backy/utils.go | 121 ++++++----- 25 files changed, 522 insertions(+), 375 deletions(-) create mode 100644 .changes/v0.4.0.md rename {docs/.frontmatter => .frontmatter}/database/mediaDb.json (100%) create mode 100644 .frontmatter/database/taxonomyDb.json create mode 100644 cmd/list.go rename docs/frontmatter.json => frontmatter.json (83%) create mode 100644 pkg/backy/list.go diff --git a/.changes/v0.4.0.md b/.changes/v0.4.0.md new file mode 100644 index 0000000..cb58cf6 --- /dev/null +++ b/.changes/v0.4.0.md @@ -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 diff --git a/docs/.frontmatter/database/mediaDb.json b/.frontmatter/database/mediaDb.json similarity index 100% rename from docs/.frontmatter/database/mediaDb.json rename to .frontmatter/database/mediaDb.json diff --git a/.frontmatter/database/taxonomyDb.json b/.frontmatter/database/taxonomyDb.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/.frontmatter/database/taxonomyDb.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 9cbee06..69e60a3 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -21,7 +21,7 @@ jobs: - run: git fetch --force --tags - uses: actions/setup-go@v4 with: - go-version: '1.20' + go-version: '1.26' cache: true # More assembly might be required: Docker logins, GPG, etc. It all depends # on your needs. diff --git a/cmd/backup.go b/cmd/backup.go index 9cf1ca9..056f018 100644 --- a/cmd/backup.go +++ b/cmd/backup.go @@ -32,10 +32,10 @@ func Backup(cmd *cobra.Command, args []string) { backyConfOpts := backy.NewOpts(cfgFile, backy.AddCommandLists(cmdLists)) backyConfOpts.InitConfig() - config := backy.ReadConfig(backyConfOpts) + backy.ReadConfig(backyConfOpts) - config.RunListConfig("", backyConfOpts) - for _, host := range config.Hosts { + backyConfOpts.RunListConfig("") + for _, host := range backyConfOpts.Hosts { if host.SshClient != nil { host.SshClient.Close() } diff --git a/cmd/exec.go b/cmd/exec.go index ffaf592..33d966c 100644 --- a/cmd/exec.go +++ b/cmd/exec.go @@ -23,7 +23,7 @@ var ( func execute(cmd *cobra.Command, args []string) { 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)) diff --git a/cmd/list.go b/cmd/list.go new file mode 100644 index 0000000..0dbf345 --- /dev/null +++ b/cmd/list.go @@ -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() +} diff --git a/cmd/root.go b/cmd/root.go index 0a4dc13..77a0ed4 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -36,5 +36,5 @@ func init() { rootCmd.PersistentFlags().StringVarP(&cfgFile, "config", "f", "", "config file to read from") rootCmd.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, "Sets verbose level") - rootCmd.AddCommand(backupCmd, execCmd, cronCmd, versionCmd) + rootCmd.AddCommand(backupCmd, execCmd, cronCmd, versionCmd, listCmd) } diff --git a/cmd/version.go b/cmd/version.go index f7a0801..7dc1c7d 100644 --- a/cmd/version.go +++ b/cmd/version.go @@ -7,7 +7,7 @@ import ( "github.com/spf13/cobra" ) -const versionStr = "0.3.1" +const versionStr = "0.4.0" var ( versionCmd = &cobra.Command{ @@ -32,9 +32,6 @@ func version(cmd *cobra.Command, args []string) { } else if vPre && !numOnly { fmt.Printf("v%s\n", versionStr) } else { - if vPre && numOnly { - fmt.Println("vpre flag and num flag both detected!") - } fmt.Printf("Backy version: %s\n", versionStr) } diff --git a/docs/content/cli/_index.md b/docs/content/cli/_index.md index 891f7c7..f0abe89 100644 --- a/docs/content/cli/_index.md +++ b/docs/content/cli/_index.md @@ -19,6 +19,7 @@ Available Commands: cron Starts a scheduler that runs lists defined in config file. exec Runs commands defined in config file in order given. help Help about any command + list Lists commands, lists, or hosts defined in config file. version Prints the version and exits Flags: @@ -91,7 +92,7 @@ Usage: Flags: -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. Global Flags: @@ -99,3 +100,20 @@ Global Flags: -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 +``` diff --git a/docs/content/config/command-lists.md b/docs/content/config/command-lists.md index e29c432..9689a73 100644 --- a/docs/content/config/command-lists.md +++ b/docs/content/config/command-lists.md @@ -16,8 +16,8 @@ The top-level object key can be anything you want but not the same as another. - test - test2 notifications: - - prod-email - - matrix + - mail.prod-email + - matrix.sysadmin 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 | | `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 | -| `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 @@ -43,7 +43,7 @@ order: 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 @@ -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. -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 %}} Note: Backy uses the second field of cron, so add anything except * to the beginning of a regular cron expression. {{% /notice %}} ```yaml -cmd-configs: -  cmds-to-run: # this can be any name you want +cmd-lists: +  docker-container-backup: # this can be any name you want     # all commands have to be defined     order:       - stop-docker-container       - backup-docker-container-script       - shell-cmd       - hostname +      - start-docker-container     notifications: -      - matrix -    name: backup-some-server +      - matrix.id +    name: backup-some-container     cron: "0 0 1 * * *"   hostname:     name: hostname     order:       - hostname     notifications: -     - prod-email +     - mail.prod-email ``` diff --git a/docs/content/config/notifications.md b/docs/content/config/notifications.md index ff711a3..c2a28d1 100644 --- a/docs/content/config/notifications.md +++ b/docs/content/config/notifications.md @@ -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/). -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 notifications: - prod-email: - type: mail - host: yourhost.tld - port: 587 - senderaddress: email@domain.tld - to: - - admin@domain.tld - username: smtp-username@domain.tld - password: your-password-here + mail: + prod-email: + host: yourhost.tld + port: 587 + senderaddress: email@domain.tld + to: + - admin@domain.tld + username: smtp-username@domain.tld + password: your-password-here matrix: - type: matrix - home-server: your-home-server.tld - room-id: room-id - access-token: your-access-token - user-id: your-user-id + matrix: + home-server: your-home-server.tld + room-id: room-id + access-token: your-access-token + 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 | --- | --- | --- @@ -47,7 +47,7 @@ The type's object and its keys are listed below. | `username` | SMTP username | `string` | `password` | SMTP password | `string` -### type: matrix +### matrix | key | description | type | --- | --- | --- diff --git a/docs/frontmatter.json b/frontmatter.json similarity index 83% rename from docs/frontmatter.json rename to frontmatter.json index e7166a2..9d8ca76 100644 --- a/docs/frontmatter.json +++ b/frontmatter.json @@ -51,17 +51,17 @@ "frontMatter.content.pageFolders": [ { "title": "content", - "path": "[[workspace]]/content", - "originalPath": "[[workspace]]/content" + "path": "[[workspace]]/docs/content", + "originalPath": "[[workspace]]/docs/content" }, { "title": "config-main", - "path": "[[workspace]]/content/config", - "originalPath": "[[workspace]]/content/config" + "path": "[[workspace]]/docs/content/config", + "originalPath": "[[workspace]]/docs/content/config" }, { "title": "gs", - "path": "[[workspace]]/content/getting-started" + "path": "[[workspace]]/docs/content/getting-started" } ] } \ No newline at end of file diff --git a/getCommandHelp b/getCommandHelp index 289bafa..40a5ca7 100755 --- a/getCommandHelp +++ b/getCommandHelp @@ -50,4 +50,11 @@ eval "${BACKYCOMMAND} version -h >> _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" \ No newline at end of file diff --git a/go.mod b/go.mod index 77ba0cc..e3ed4f0 100644 --- a/go.mod +++ b/go.mod @@ -3,29 +3,33 @@ module git.andrewnw.xyz/CyberShell/backy go 1.26 require ( - github.com/go-co-op/gocron v1.30.1 - github.com/hashicorp/vault/api v1.9.2 + github.com/go-co-op/gocron v1.33.1 + github.com/hashicorp/vault/api v1.10.0 github.com/joho/godotenv v1.5.1 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/nikoksr/notify v0.41.0 github.com/pkg/errors v0.9.1 github.com/rs/zerolog v1.30.0 github.com/spf13/cobra v1.7.0 - github.com/spf13/viper v1.16.0 - go.mongodb.org/mongo-driver v1.12.0 - golang.org/x/crypto v0.11.0 + go.mongodb.org/mongo-driver v1.12.1 + golang.org/x/crypto v0.13.0 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 ) require ( github.com/cenkalti/backoff/v3 v3.2.2 // 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/go-jose/go-jose/v3 v3.0.0 // 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/go-cleanhttp v0.5.2 // 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-secure-stdlib/parseutil v0.1.7 // 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/inconshreveable/mousetrap v1.1.0 // indirect github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible // 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/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/mapstructure v1.5.0 // indirect + github.com/mitchellh/reflectwalk v1.0.2 // 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/robfig/cron/v3 v3.0.1 // 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/jwalterweatherman v1.1.0 // 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/subosito/gotenv v1.4.2 // indirect - github.com/tidwall/gjson v1.15.0 // indirect + github.com/subosito/gotenv v1.6.0 // indirect + github.com/tidwall/gjson v1.16.0 // indirect github.com/tidwall/match v1.1.1 // indirect github.com/tidwall/pretty v1.2.1 // 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/stringprep v1.0.4 // indirect github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a // indirect - go.uber.org/atomic v1.9.0 // indirect - golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1 // indirect - golang.org/x/net v0.12.0 // indirect + go.mau.fi/util v0.0.0-20230906155759-14bad39a8718 // indirect + go.uber.org/atomic v1.11.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/sys v0.10.0 // indirect - golang.org/x/text v0.11.0 // indirect + golang.org/x/sys v0.12.0 // indirect + golang.org/x/text v0.13.0 // indirect golang.org/x/time v0.3.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/go.sum b/go.sum index e1bdb41..c7e94fb 100644 --- a/go.sum +++ b/go.sum @@ -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/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= 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/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= 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/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/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= @@ -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/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.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.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= 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-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.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.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/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/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-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= 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.16.7 h1:2mk3MPGNzKyxErAw8YaohYh69+pa4sIQSC0fPGCFR9I= 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/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 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/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= 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/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= 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.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= 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.7.1 h1:etflOAAHORrCC44V+aR6Ftzort912ZU+YLiSTuV8eaE= 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/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.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/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 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.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.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.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 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/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.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.15.0 h1:5n/pM+v3r5ujuNl4YLZLsQ+UE5jlkLVm7jMzT5Mpolw= 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/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= 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.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 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/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.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= 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.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.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-20190510104115-cbcb75029529/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.11.0 h1:6Ewdq3tDic1mg5xRO4milcWCfMVQhI4NkqWWvqejpuA= 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-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 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-20230713183714-613f0c0eb8a1 h1:MGwJjxBy0HJshjDNfLsYO8xppfqWlA5ZT9OhtUUhTNw= 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-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= 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.12.0 h1:cfawfvKITfUsFCeJIHJrbSxpeu/E81khclypR0GVT50= 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-20190226205417-e64efc72b421/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.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA= 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-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 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.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4= 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-20190308202827-9d24e82272b4/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/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.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/go.mod h1:K2gwkaesF/D7av7Kxl0HbF5kGOd2ArupNTX3X44+8l8= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= diff --git a/pkg/backy/backy.go b/pkg/backy/backy.go index cecd44a..59947bd 100644 --- a/pkg/backy/backy.go +++ b/pkg/backy/backy.go @@ -17,12 +17,13 @@ import ( "embed" "github.com/rs/zerolog" + "github.com/rs/zerolog/log" ) //go:embed templates/*.txt var templates embed.FS -var requiredKeys = []string{"commands", "cmd-configs"} +var requiredKeys = []string{"commands"} 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 // variables specified in the Env file or Environment. // 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 ( outputArr []string @@ -52,13 +53,13 @@ func (command *Command) RunCmd(log zerolog.Logger, backyConf *ConfigFile, opts * command.Type = strings.TrimSpace(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 { - 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 { - err := command.RemoteHost.ConnectToSSHHost(opts, backyConf) + err := command.RemoteHost.ConnectToSSHHost(opts) if err != nil { 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 if err != nil { - connErr := command.RemoteHost.ConnectToSSHHost(opts, backyConf) + connErr := command.RemoteHost.ConnectToSSHHost(opts) if connErr != nil { 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() - injectEnvIntoSSH(envVars, commandSession, opts, log) + injectEnvIntoSSH(envVars, commandSession, opts, cmdCtxLogger) cmd := command.Cmd for _, a := range command.Args { 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? defer func() { 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 { outputArr = append(outputArr, str) } - log.Info().Fields(outMap).Send() + cmdCtxLogger.Info().Fields(outMap).Send() } return outputArr, err } @@ -211,7 +212,7 @@ func (command *Command) RunCmd(log zerolog.Logger, backyConf *ConfigFile, opts * if str, ok := outMap["output"].(string); ok { outputArr = append(outputArr, str) } - log.Info().Fields(outMap).Send() + cmdCtxLogger.Info().Fields(outMap).Send() } return outputArr, err } @@ -224,7 +225,7 @@ func (command *Command) RunCmd(log zerolog.Logger, backyConf *ConfigFile, opts * if str, ok := outMap["output"].(string); ok { outputArr = append(outputArr, str) } - log.Info().Fields(outMap).Send() + cmdCtxLogger.Info().Fields(outMap).Send() } return outputArr, nil } @@ -240,18 +241,18 @@ func (command *Command) RunCmd(log zerolog.Logger, backyConf *ConfigFile, opts * if str, ok := outMap["output"].(string); ok { outputArr = append(outputArr, str) } - log.Info().Fields(outMap).Send() + cmdCtxLogger.Info().Fields(outMap).Send() } 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 } } else { var err error 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) @@ -260,7 +261,7 @@ func (command *Command) RunCmd(log zerolog.Logger, backyConf *ConfigFile, opts * if command.Dir != nil { localCMD.Dir = *command.Dir } - injectEnvIntoLocalCMD(envVars, localCMD, log) + injectEnvIntoLocalCMD(envVars, localCMD, cmdCtxLogger) cmdOutWriters = io.MultiWriter(&cmdOutBuf) @@ -286,12 +287,12 @@ func (command *Command) RunCmd(log zerolog.Logger, backyConf *ConfigFile, opts * } 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, 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...) if command.Dir != nil { @@ -299,7 +300,7 @@ func (command *Command) RunCmd(log zerolog.Logger, backyConf *ConfigFile, opts * } // fmt.Printf("%v\n", envVars.env) - injectEnvIntoLocalCMD(envVars, localCMD, log) + injectEnvIntoLocalCMD(envVars, localCMD, cmdCtxLogger) cmdOutWriters = io.MultiWriter(&cmdOutBuf) // 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 { outputArr = append(outputArr, str) } - log.Info().Fields(outMap).Send() + cmdCtxLogger.Info().Fields(outMap).Send() } 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, 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 { fieldsMap := make(map[string]interface{}) fieldsMap["list"] = list.Name - cmdLog := config.Logger.Info() + cmdLog := opts.Logger.Info() var count int var cmdsRan []string var outStructArr []outStruct for _, cmd := range list.Order { - currentCmd := config.Cmds[cmd].Cmd + currentCmd := opts.Cmds[cmd].Cmd - fieldsMap["cmd"] = config.Cmds[cmd].Cmd - cmdToRun := config.Cmds[cmd] + fieldsMap["cmd"] = opts.Cmds[cmd].Cmd + cmdToRun := opts.Cmds[cmd] cmdLog.Fields(fieldsMap).Send() - cmdLogger := config.Logger.With(). + cmdLogger := opts.Logger.With(). Str("backy-cmd", cmd).Str("Host", "local machine"). Logger() if cmdToRun.Host != nil { - cmdLogger = config.Logger.With(). + cmdLogger = opts.Logger.With(). Str("backy-cmd", cmd).Str("Host", *cmdToRun.Host). Logger() } - outputArr, runOutErr := cmdToRun.RunCmd(cmdLogger, config, opts) + outputArr, runOutErr := cmdToRun.RunCmd(cmdLogger, opts) if list.NotifyConfig != nil { if cmdToRun.GetOutput || list.GetOutput { @@ -380,7 +381,7 @@ func cmdListWorker(msgTemps *msgTemplates, jobs <-chan *CmdList, config *ConfigF errStruct["listName"] = list.Name errStruct["Command"] = currentCmd errStruct["Cmd"] = cmd - errStruct["Args"] = config.Cmds[cmd].Args + errStruct["Args"] = opts.Cmds[cmd].Args errStruct["Err"] = runOutErr errStruct["CmdsRan"] = cmdsRan errStruct["Output"] = outputArr @@ -390,17 +391,17 @@ func cmdListWorker(msgTemps *msgTemplates, jobs <-chan *CmdList, config *ConfigF tmpErr := msgTemps.err.Execute(&errMsg, errStruct) 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 { - config.Logger.Err(notifySendErr).Send() + opts.Logger.Err(notifySendErr).Send() } } - config.Logger.Err(runOutErr).Send() + opts.Logger.Err(runOutErr).Send() break } else { @@ -420,14 +421,14 @@ func cmdListWorker(msgTemps *msgTemplates, jobs <-chan *CmdList, config *ConfigF tmpErr := msgTemps.success.Execute(&successMsg, successStruct) if tmpErr != nil { - config.Logger.Err(tmpErr).Send() + opts.Logger.Err(tmpErr).Send() break } err := list.NotifyConfig.Send(context.Background(), fmt.Sprintf("List %s succeded", list.Name), successMsg.String()) if err != nil { - config.Logger.Err(err).Send() + opts.Logger.Err(err).Send() } } } else { @@ -442,22 +443,22 @@ func cmdListWorker(msgTemps *msgTemplates, jobs <-chan *CmdList, config *ConfigF } // RunListConfig runs a command list from the ConfigFile. -func (config *ConfigFile) RunListConfig(cron string, opts *ConfigOpts) { +func (opts *ConfigOpts) RunListConfig(cron string) { mTemps := &msgTemplates{ err: template.Must(template.New("error.txt").ParseFS(templates, "templates/error.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) results := make(chan string) // This starts up list workers, initially blocked // because there are no jobs yet. 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 == "" { cmdConfig.Name = listName } @@ -475,26 +476,26 @@ func (config *ConfigFile) RunListConfig(cron string, opts *ConfigOpts) { <-results } - config.closeHostConnections() + opts.closeHostConnections() } -func (config *ConfigFile) ExecuteCmds(opts *ConfigOpts) { +func (config *ConfigOpts) ExecuteCmds(opts *ConfigOpts) { for _, cmd := range opts.executeCmds { - cmdToRun := config.Cmds[cmd] - cmdLogger := config.Logger.With(). + cmdToRun := opts.Cmds[cmd] + cmdLogger := opts.Logger.With(). Str("backy-cmd", cmd). Logger() - _, runErr := cmdToRun.RunCmd(cmdLogger, config, opts) + _, runErr := cmdToRun.RunCmd(cmdLogger, opts) 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 { c.Logger.Info().Str("server", host.HostName) if host.isProxyHost { diff --git a/pkg/backy/config.go b/pkg/backy/config.go index 56f0af1..bace418 100644 --- a/pkg/backy/config.go +++ b/pkg/backy/config.go @@ -10,40 +10,58 @@ import ( "git.andrewnw.xyz/CyberShell/backy/pkg/logging" 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/rs/zerolog" - "github.com/spf13/viper" ) +var homeDir string +var homeDirErr error +var backyHomeConfDir string +var configFiles []string + 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) if err != 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 { - backyViper.SetConfigName("backy.yml") // name of config file (with extension) - backyViper.SetConfigName("backy.yaml") // name of config file (with extension) - backyViper.SetConfigType("yaml") // REQUIRED if the config file does not have the extension in the name - backyViper.AddConfigPath(".") // optionally look for config in the working directory - backyViper.AddConfigPath("$HOME/.config/backy") // call multiple times to add many search paths - } - err := backyViper.ReadInConfig() // Find and read the config file - if err != nil { // Handle errors reading the config file - msg := fmt.Sprintf("fatal error reading config file %s: %v", backyViper.ConfigFileUsed(), err) - logging.ExitWithMSG(msg, 1, nil) - } - opts.viper = backyViper + + cFileFalures := 0 + for _, c := range configFiles { + if err := backyKoanf.Load(file.Provider(c), yaml.Parser()); err != nil { + cFileFalures++ + } else { + opts.ConfigFilePath = c + break + } + } + if cFileFalures == len(configFiles) { + 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. -func ReadConfig(opts *ConfigOpts) *ConfigFile { +func ReadConfig(opts *ConfigOpts) *ConfigOpts { if isatty.IsTerminal(os.Stdout.Fd()) { os.Setenv("BACKY_TERM", "enabled") @@ -53,44 +71,38 @@ func ReadConfig(opts *ConfigOpts) *ConfigFile { os.Setenv("BACKY_TERM", "disabled") } - backyConfigFile := NewConfig() - backyViper := opts.viper - opts.loadEnv() - // envFileInConfigDir := fmt.Sprintf("%s/.env", path.Dir(backyViper.ConfigFileUsed())) + backyKoanf := opts.koanf - // load the .env file in config file directory - // _ = godotenv.Load(envFileInConfigDir) + opts.loadEnv() - if backyViper.GetBool(getNestedConfig("logging", "cmd-std-out")) { + if backyKoanf.Bool(getNestedConfig("logging", "cmd-std-out")) { os.Setenv("BACKY_STDOUT", "enabled") } - CheckConfigValues(backyViper) + CheckConfigValues(backyKoanf, opts.ConfigFilePath) for _, c := range opts.executeCmds { - if !backyViper.IsSet(getCmdFromConfig(c)) { - logging.ExitWithMSG(Sprintf("command %s is not in config file %s", c, backyViper.ConfigFileUsed()), 1, nil) + if !backyKoanf.Exists(getCmdFromConfig(c)) { + logging.ExitWithMSG(Sprintf("command %s is not in config file %s", c, opts.ConfigFilePath), 1, nil) } } 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) } } var ( - // backyLoggingOpts *viper.Viper verbose bool logFile string ) - verbose = backyViper.GetBool(getLoggingKeyFromConfig("verbose")) + verbose = backyKoanf.Bool(getLoggingKeyFromConfig("verbose")) - logFile = fmt.Sprintf("%s/backy.log", path.Dir(backyViper.ConfigFileUsed())) - if backyViper.IsSet(getLoggingKeyFromConfig("file")) { - logFile = backyViper.GetString(getLoggingKeyFromConfig("file")) + logFile = fmt.Sprintf("%s/backy.log", path.Dir(opts.ConfigFilePath)) + if backyKoanf.Exists(getLoggingKeyFromConfig("file")) { + logFile = backyKoanf.String(getLoggingKeyFromConfig("file")) } - zerolog.SetGlobalLevel(zerolog.InfoLevel) if verbose { @@ -99,7 +111,7 @@ func ReadConfig(opts *ConfigOpts) *ConfigFile { 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") // Other qualifiers can go here as well @@ -111,42 +123,30 @@ func ReadConfig(opts *ConfigOpts) *ConfigFile { 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() - commandsMap := backyViper.GetStringMapString("commands") - commandsMapViper := backyViper.Sub("commands") - unmarshalErr := commandsMapViper.Unmarshal(&backyConfigFile.Cmds) + unmarshalErr := backyKoanf.UnmarshalWithConf("commands", &opts.Cmds, koanf.UnmarshalConf{Tag: "yaml"}) if unmarshalErr != nil { panic(fmt.Errorf("error unmarshalling cmds struct: %w", unmarshalErr)) } - hostConfigsMap := make(map[string]*viper.Viper) - - for cmdName, cmdConf := range backyConfigFile.Cmds { + for cmdName, cmdConf := range opts.Cmds { envFileErr := testFile(cmdConf.Env) if envFileErr != nil { - backyConfigFile.Logger.Info().Str("cmd", cmdName).Err(envFileErr).Send() + opts.Logger.Info().Str("cmd", cmdName).Err(envFileErr).Send() os.Exit(1) } 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 = hostsMapViper.Unmarshal(&backyConfigFile.Hosts) + unmarshalErr = backyKoanf.UnmarshalWithConf("hosts", &opts.Hosts, koanf.UnmarshalConf{Tag: "yaml"}) if unmarshalErr != nil { panic(fmt.Errorf("error unmarshalling hosts struct: %w", unmarshalErr)) } - for hostConfigName, host := range backyConfigFile.Hosts { + for hostConfigName, host := range opts.Hosts { if host.Host == "" { host.Host = hostConfigName } @@ -155,7 +155,7 @@ func ReadConfig(opts *ConfigOpts) *ConfigFile { if len(proxyHosts) > 1 { for hostNum, h := range proxyHosts { if hostNum > 1 { - proxyHost, defined := backyConfigFile.Hosts[h] + proxyHost, defined := opts.Hosts[h] if defined { host.ProxyHost = append(host.ProxyHost, proxyHost) } else { @@ -163,7 +163,7 @@ func ReadConfig(opts *ConfigOpts) *ConfigFile { host.ProxyHost = append(host.ProxyHost, newProxy) } } else { - proxyHost, defined := backyConfigFile.Hosts[h] + proxyHost, defined := opts.Hosts[h] if defined { host.ProxyHost = append(host.ProxyHost, proxyHost) } else { @@ -173,7 +173,7 @@ func ReadConfig(opts *ConfigOpts) *ConfigFile { } } } else { - proxyHost, defined := backyConfigFile.Hosts[proxyHosts[0]] + proxyHost, defined := opts.Hosts[proxyHosts[0]] if defined { host.ProxyHost = append(host.ProxyHost, proxyHost) } else { @@ -184,33 +184,29 @@ func ReadConfig(opts *ConfigOpts) *ConfigFile { } } - cmdListCfg := backyViper.Sub("cmd-configs") - unmarshalErr = cmdListCfg.Unmarshal(&backyConfigFile.CmdConfigLists) - if unmarshalErr != nil { - panic(fmt.Errorf("error unmarshalling cmd list struct: %w", unmarshalErr)) + if backyKoanf.Exists("cmd-lists") { + unmarshalErr = backyKoanf.UnmarshalWithConf("cmd-lists", &opts.CmdConfigLists, koanf.UnmarshalConf{Tag: "yaml"}) + if unmarshalErr != nil { + logging.ExitWithMSG((fmt.Sprintf("error unmarshalling cmd list struct: %v", unmarshalErr)), 1, &opts.Logger) + } } var cmdNotFoundSliceErr []error - for cmdListName, cmdList := range backyConfigFile.CmdConfigLists { + for cmdListName, cmdList := range opts.CmdConfigLists { if opts.useCron { cron := strings.TrimSpace(cmdList.Cron) if cron == "" { - delete(backyConfigFile.CmdConfigLists, cmdListName) + delete(opts.CmdConfigLists, cmdListName) } } for _, cmdInList := range cmdList.Order { - _, cmdNameFound := backyConfigFile.Cmds[cmdInList] + _, cmdNameFound := opts.Cmds[cmdInList] if !cmdNameFound { cmdNotFoundStr := fmt.Sprintf("command %s in list %s is not defined in commands section in config file", cmdInList, cmdListName) cmdNotFoundErr := errors.New(cmdNotFoundStr) 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 { @@ -218,39 +214,35 @@ func ReadConfig(opts *ConfigOpts) *ConfigFile { 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) } - for c := range commandsMap { + for c := range opts.Cmds { if opts.executeCmds != nil && !contains(opts.executeCmds, c) { - delete(backyConfigFile.Cmds, c) + delete(opts.Cmds, c) } } if len(opts.executeLists) > 0 { - for l := range backyConfigFile.CmdConfigLists { + for l := range opts.CmdConfigLists { if !contains(opts.executeLists, l) { - delete(backyConfigFile.CmdConfigLists, l) + delete(opts.CmdConfigLists, l) } } } - if backyViper.IsSet("notifications") { - notificationsMap := backyViper.GetStringMap("notifications") - for id := range notificationsMap { - notifConfig := backyViper.Sub(getNestedConfig("notifications", id)) - config := &NotificationsConfig{ - Config: notifConfig, - Enabled: true, - } - backyConfigFile.Notifications[id] = config + if backyKoanf.Exists("notifications") { + + unmarshalErr = backyKoanf.UnmarshalWithConf("notifications", &opts.NotificationConf, koanf.UnmarshalConf{Tag: "yaml"}) + if unmarshalErr != nil { + fmt.Printf("error unmarshalling notifications object: %v", unmarshalErr) } } - for _, cmd := range backyConfigFile.Cmds { + for _, cmd := range opts.Cmds { if cmd.Host != nil { - host, hostFound := backyConfigFile.Hosts[*cmd.Host] + host, hostFound := opts.Hosts[*cmd.Host] if hostFound { cmd.RemoteHost = host cmd.RemoteHost.Host = host.Host @@ -258,19 +250,18 @@ func ReadConfig(opts *ConfigOpts) *ConfigFile { cmd.RemoteHost.HostName = host.HostName } } else { - backyConfigFile.Hosts[*cmd.Host] = &Host{Host: *cmd.Host} + opts.Hosts[*cmd.Host] = &Host{Host: *cmd.Host} cmd.RemoteHost = &Host{Host: *cmd.Host} } } } - backyConfigFile.SetupNotify() - opts.ConfigFile = backyConfigFile + opts.SetupNotify() if err := opts.setupVault(); err != nil { log.Err(err).Send() } - opts.ConfigFile = backyConfigFile - return backyConfigFile + + return opts } func getNestedConfig(nestedConfig, key string) string { @@ -289,16 +280,16 @@ func getLoggingKeyFromConfig(key 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 { - if !opts.viper.GetBool("vault.enabled") { + if !opts.koanf.Bool("vault.enabled") { return nil } config := vault.DefaultConfig() - config.Address = opts.viper.GetString("vault.address") + config.Address = opts.koanf.String("vault.address") if strings.TrimSpace(config.Address) == "" { config.Address = os.Getenv("VAULT_ADDR") } @@ -308,7 +299,7 @@ func (opts *ConfigOpts) setupVault() error { return err } - token := opts.viper.GetString("vault.token") + token := opts.koanf.String("vault.token") if strings.TrimSpace(token) == "" { token = os.Getenv("VAULT_TOKEN") } @@ -318,10 +309,9 @@ func (opts *ConfigOpts) setupVault() error { client.SetToken(token) - cmdListCfg := opts.viper.Sub("viper.keys") - unmarshalErr := cmdListCfg.Unmarshal(&opts.VaultKeys) + unmarshalErr := opts.koanf.UnmarshalWithConf("vault.keys", &opts.VaultKeys, koanf.UnmarshalConf{Tag: "yaml"}) 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 diff --git a/pkg/backy/cron.go b/pkg/backy/cron.go index 89a5f92..dda5b25 100644 --- a/pkg/backy/cron.go +++ b/pkg/backy/cron.go @@ -5,31 +5,34 @@ package backy import ( + "fmt" "strings" "time" + "git.andrewnw.xyz/CyberShell/backy/pkg/logging" "github.com/go-co-op/gocron" ) func (opts *ConfigOpts) Cron() { s := gocron.NewScheduler(time.Local) s.TagsUnique() - cmdLists := opts.ConfigFile.CmdConfigLists + cmdLists := opts.CmdConfigLists for listName, config := range cmdLists { if config.Name == "" { config.Name = listName } + cron := strings.TrimSpace(config.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) { - opts.ConfigFile.RunListConfig(cron, opts) + opts.RunListConfig(cron) }, cron) 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() } diff --git a/pkg/backy/list.go b/pkg/backy/list.go new file mode 100644 index 0000000..df6dcd2 --- /dev/null +++ b/pkg/backy/list.go @@ -0,0 +1,5 @@ +package backy + +func (opts *ConfigOpts) ListConfiguration() { + +} diff --git a/pkg/backy/mongo.go b/pkg/backy/mongo.go index 4e8d1eb..a2962a9 100644 --- a/pkg/backy/mongo.go +++ b/pkg/backy/mongo.go @@ -18,7 +18,7 @@ const mongoConfigKey = "global.mongo" func (opts *ConfigOpts) InitMongo() { - if !opts.viper.GetBool(getMongoConfigKey("enabled")) { + if !opts.koanf.Bool(getMongoConfigKey("enabled")) { return } var ( @@ -27,37 +27,37 @@ func (opts *ConfigOpts) InitMongo() { ) // TODO: Get uri and creditials from config - host := opts.viper.GetString(getMongoConfigKey("host")) - port := opts.viper.GetInt32(getMongoConfigKey("port")) + host := opts.koanf.String(getMongoConfigKey("host")) + port := opts.koanf.Int64(getMongoConfigKey("port")) ctx, ctxCancel := context.WithTimeout(context.Background(), 10*time.Second) defer ctxCancel() client, err = mongo.Connect(ctx, options.Client().ApplyURI(fmt.Sprintf("mongo://%s:%d", host, port))) - if opts.viper.GetBool(getMongoConfigKey("prod")) { - mongoEnvFileSet := opts.viper.IsSet(getMongoConfigKey("env")) + if opts.koanf.Bool(getMongoConfigKey("prod")) { + mongoEnvFileSet := opts.koanf.Exists(getMongoConfigKey("env")) if mongoEnvFileSet { getMongoConfigFromEnv(opts) } auth := options.Credential{} - auth.Password = opts.viper.GetString("global.mongo.password") - auth.Username = opts.viper.GetString("global.mongo.username") + auth.Password = opts.koanf.String("global.mongo.password") + auth.Username = opts.koanf.String("global.mongo.username") client, err = mongo.Connect(ctx, options.Client().SetAuth(auth).ApplyURI("mongodb://localhost:27017")) } if err != nil { - opts.ConfigFile.Logger.Fatal().Err(err).Send() + opts.Logger.Fatal().Err(err).Send() } if err != nil { - opts.ConfigFile.Logger.Fatal().Err(err).Send() + opts.Logger.Fatal().Err(err).Send() } defer client.Disconnect(ctx) err = client.Ping(ctx, readpref.Primary()) if err != nil { - opts.ConfigFile.Logger.Fatal().Err(err).Send() + opts.Logger.Fatal().Err(err).Send() } databases, err := client.ListDatabaseNames(ctx, bson.M{}) if err != nil { - opts.ConfigFile.Logger.Fatal().Err(err).Send() + opts.Logger.Fatal().Err(err).Send() } fmt.Println(databases) backyDB := client.Database("backy") @@ -68,7 +68,7 @@ func (opts *ConfigOpts) InitMongo() { } 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 { return err } @@ -84,8 +84,8 @@ func getMongoConfigFromEnv(opts *ConfigOpts) error { if !mongoUserFound { return errors.New("MONGO_PASSWORD not set in " + mongoEnvFile.Name()) } - opts.viper.Set(mongoConfigKey+".password", mongoPW) - opts.viper.Set(mongoConfigKey+".username", mongoUser) + opts.koanf.Set(mongoConfigKey+".password", mongoPW) + opts.koanf.Set(mongoConfigKey+".username", mongoUser) return nil } diff --git a/pkg/backy/notification.go b/pkg/backy/notification.go index e1ec2a1..fc29e3e 100644 --- a/pkg/backy/notification.go +++ b/pkg/backy/notification.go @@ -5,67 +5,76 @@ package backy import ( "fmt" + "strings" + "git.andrewnw.xyz/CyberShell/backy/pkg/logging" "github.com/nikoksr/notify" "github.com/nikoksr/notify/service/mail" "github.com/nikoksr/notify/service/matrix" "maunium.net/go/mautrix/id" ) -type matrixStruct struct { - homeserver string - roomid id.RoomID - accessToken string - userId id.UserID +type MatrixStruct struct { + Homeserver string `yaml:"homeserver"` + Roomid id.RoomID `yaml:"room-id"` + AccessToken string `yaml:"access-token"` + UserId id.UserID `yaml:"user-id"` } -type mailConfig struct { - senderaddress string - host string - to []string - username string - password string - port string -} - -func SetupCommandsNotifiers(backyConfig ConfigFile, ids ...string) { - +type MailConfig struct { + Host string `yaml:"host"` + Port string `yaml:"port"` + Username string `yaml:"username"` + SenderAddress string `yaml:"senderaddress"` + To []string `yaml:"to"` + Password string `yaml:"password"` } // 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 - for notifyID := range backyConfig.Notifications { - if contains(cmdConfig.Notifications, notifyID) { + for _, id := range cmdConfig.Notifications { + 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 { - config := backyConfig.Notifications[notifyID].Config - switch config.GetString("type") { - case "matrix": - mtrx := matrixStruct{ - userId: id.UserID(config.GetString("user-id")), - roomid: id.RoomID(config.GetString("room-id")), - accessToken: config.GetString("access-token"), - homeserver: config.GetString("homeserver"), - } - mtrxClient, _ := setupMatrix(mtrx) - services = append(services, mtrxClient) - case "mail": - mailCfg := mailConfig{ - senderaddress: config.GetString("senderaddress"), - password: config.GetString("password"), - username: config.GetString("username"), - to: config.GetStringSlice("to"), - host: config.GetString("host"), - port: fmt.Sprint(config.GetUint16("port")), - } - mailClient := setupMail(mailCfg) - services = append(services, mailClient) - } + confSplit := strings.Split(id, ".") + confType := confSplit[0] + confId := confSplit[1] + switch confType { + + case "mail": + conf, ok := opts.NotificationConf.MailConfig[confId] + if !ok { + opts.Logger.Info().Err(fmt.Errorf("error: ID %s not found in mail object", confId)).Str("list", confName).Send() + continue + } + mailConf := setupMail(conf) + services = append(services, mailConf) + case "matrix": + conf, ok := opts.NotificationConf.MatrixConfig[confId] + if !ok { + opts.Logger.Info().Err(fmt.Errorf("error: ID %s not found in matrix object", confId)).Str("list", confName).Send() + continue } + 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...) @@ -74,19 +83,18 @@ func (backyConfig *ConfigFile) SetupNotify() { // logging.ExitWithMSG("This was a test of notifications", 0, nil) } -func setupMatrix(config matrixStruct) (*matrix.Matrix, error) { - matrixClient, matrixErr := matrix.New(config.userId, config.roomid, config.homeserver, config.accessToken) +func setupMatrix(config MatrixStruct) (*matrix.Matrix, error) { + matrixClient, matrixErr := matrix.New(config.UserId, config.Roomid, config.Homeserver, config.AccessToken) if matrixErr != nil { - panic(matrixErr) + return nil, matrixErr } return matrixClient, nil - } -func setupMail(config mailConfig) *mail.Mail { - mailClient := mail.New(config.senderaddress, config.host+":"+config.port) - mailClient.AuthenticateSMTP("", config.username, config.password, config.host) - mailClient.AddReceivers(config.to...) +func setupMail(config MailConfig) *mail.Mail { + mailClient := mail.New(config.SenderAddress, config.Host+":"+config.Port) + mailClient.AuthenticateSMTP("", config.Username, config.Password, config.Host) + mailClient.AddReceivers(config.To...) mailClient.BodyFormat(mail.PlainText) return mailClient } diff --git a/pkg/backy/ssh.go b/pkg/backy/ssh.go index 7430fd4..ad33035 100644 --- a/pkg/backy/ssh.go +++ b/pkg/backy/ssh.go @@ -28,7 +28,7 @@ var TS = strings.TrimSpace // 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 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 connectErr error @@ -47,6 +47,11 @@ func (remoteConfig *Host) ConnectToSSHHost(opts *ConfigOpts, config *ConfigFile) var configFile *os.File var sshConfigFileOpenErr error if !remoteConfig.useDefaultConfig { + var err error + remoteConfig.ConfigFilePath, err = resolveDir(remoteConfig.ConfigFilePath) + if err != nil { + return err + } configFile, sshConfigFileOpenErr = os.Open(remoteConfig.ConfigFilePath) if sshConfigFileOpenErr != nil { return sshConfigFileOpenErr @@ -66,14 +71,14 @@ func (remoteConfig *Host) ConnectToSSHHost(opts *ConfigOpts, config *ConfigFile) return decodeErr } - err := remoteConfig.GetProxyJumpFromConfig(config.Hosts) + err := remoteConfig.GetProxyJumpFromConfig(opts.Hosts) if err != nil { return err } if remoteConfig.ProxyHost != nil { for _, proxyHost := range remoteConfig.ProxyHost { - err := proxyHost.GetProxyJumpConfig(config.Hosts, opts) - opts.ConfigFile.Logger.Info().Msgf("Proxy host: %s", proxyHost.Host) + err := proxyHost.GetProxyJumpConfig(opts.Hosts, opts) + opts.Logger.Info().Msgf("Proxy host: %s", proxyHost.Host) if err != nil { return err } @@ -101,24 +106,24 @@ func (remoteConfig *Host) ConnectToSSHHost(opts *ConfigOpts, config *ConfigFile) return errors.Wrap(err, "could not create hostkeycallback function") } 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 { return connectErr } if remoteConfig.SshClient != nil { - config.Hosts[remoteConfig.Host] = remoteConfig + opts.Hosts[remoteConfig.Host] = remoteConfig 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) if connectErr != nil { return connectErr } - config.Hosts[remoteConfig.Host] = remoteConfig + opts.Hosts[remoteConfig.Host] = remoteConfig return nil } @@ -148,7 +153,7 @@ func (remoteHost *Host) GetAuthMethods(opts *ConfigOpts) error { if err != nil { return err } - remoteHost.PrivateKeyPassword, err = GetPrivateKeyPassword(remoteHost.PrivateKeyPassword, opts, opts.ConfigFile.Logger) + remoteHost.PrivateKeyPassword, err = GetPrivateKeyPassword(remoteHost.PrivateKeyPassword, opts, opts.Logger) if err != nil { return err } @@ -167,7 +172,7 @@ func (remoteHost *Host) GetAuthMethods(opts *ConfigOpts) error { } } 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 { return err } @@ -297,7 +302,7 @@ func GetPrivateKeyPassword(key string, opts *ConfigOpts, log zerolog.Logger) (st } else { prKeyPassword = key } - prKeyPassword = GetVaultKey(prKeyPassword, opts, opts.ConfigFile.Logger) + prKeyPassword = GetVaultKey(prKeyPassword, opts, opts.Logger) return prKeyPassword, nil } @@ -328,7 +333,7 @@ func GetPassword(pass string, opts *ConfigOpts, log zerolog.Logger) (string, err } else { password = pass } - password = GetVaultKey(password, opts, opts.ConfigFile.Logger) + password = GetVaultKey(password, opts, opts.Logger) return password, nil } diff --git a/pkg/backy/types.go b/pkg/backy/types.go index 7e499e6..351c296 100644 --- a/pkg/backy/types.go +++ b/pkg/backy/types.go @@ -6,9 +6,9 @@ import ( vaultapi "github.com/hashicorp/vault/api" "github.com/kevinburke/ssh_config" + "github.com/knadh/koanf/v2" "github.com/nikoksr/notify" "github.com/rs/zerolog" - "github.com/spf13/viper" "go.mongodb.org/mongo-driver/bson/primitive" "go.mongodb.org/mongo-driver/mongo" "golang.org/x/crypto/ssh" @@ -37,7 +37,7 @@ type ( // Host defines a host to which to connect. // If not provided, the values will be looked up in the default ssh config files Host struct { - ConfigFilePath string `yaml:"configfilepath,omitempty"` + ConfigFilePath string `yaml:"config,omitempty"` Host string `yaml:"host,omitempty"` HostName string `yaml:"hostname,omitempty"` KnownHostsFile string `yaml:"knownhostsfile,omitempty"` @@ -113,35 +113,26 @@ type ( Notifications []string `yaml:"notifications,omitempty"` GetOutput bool `yaml:"getOutput,omitempty"` NotifyConfig *notify.Notify - // NotificationsConfig map[string]*NotificationsConfig - // NotifyConfig map[string]*notify.Notify } - ConfigFile struct { - + ConfigOpts struct { // Cmds holds the commands for a list. // Key is the name of the command, Cmds map[string]*Command `yaml:"commands"` // CmdConfigLists holds the lists of commands to be run in order. // 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. // key is the host. Hosts map[string]*Host `yaml:"hosts"` - // Notifications holds the config for different notifications. - Notifications map[string]*NotificationsConfig - Logger zerolog.Logger - } - ConfigOpts struct { // Global log level BackyLogLvl *string - // Holds config file - ConfigFile *ConfigFile + // Holds config file ConfigFilePath string @@ -160,9 +151,13 @@ type ( vaultClient *vaultapi.Client + List ListConfig + VaultKeys []*VaultKey `yaml:"keys"` - viper *viper.Viper + koanf *koanf.Koanf + + NotificationConf *Notifications `yaml:"notifications"` } outStruct struct { @@ -185,9 +180,9 @@ type ( Keys []*VaultKey `yaml:"keys"` } - NotificationsConfig struct { - Config *viper.Viper - Enabled bool + Notifications struct { + MailConfig map[string]MailConfig `yaml:"mail,omitempty"` + MatrixConfig map[string]MatrixStruct `yaml:"matrix,omitempty"` } CmdOutput struct { @@ -204,4 +199,10 @@ type ( success *template.Template err *template.Template } + + ListConfig struct { + Lists []string + Commands []string + Hosts []string + } ) diff --git a/pkg/backy/utils.go b/pkg/backy/utils.go index 5345f94..8379259 100644 --- a/pkg/backy/utils.go +++ b/pkg/backy/utils.go @@ -15,12 +15,65 @@ import ( "git.andrewnw.xyz/CyberShell/backy/pkg/logging" "github.com/joho/godotenv" + "github.com/knadh/koanf/v2" "github.com/rs/zerolog" - "github.com/spf13/viper" "golang.org/x/crypto/ssh" "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) { if envVarsToInject.file != "" { envPath, envPathErr := resolveDir(envVarsToInject.file) @@ -94,12 +147,12 @@ func contains(s []string, e string) bool { return false } -func CheckConfigValues(config *viper.Viper) { +func CheckConfigValues(config *koanf.Koanf, file string) { for _, key := range requiredKeys { - isKeySet := config.IsSet(key) + isKeySet := config.Exists(key) 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 } -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 { return os.Getenv("BACKY_TERM") == "enabled" } @@ -203,7 +198,7 @@ func resolveDir(path string) (string, error) { } 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 backyEnv, envFileErr := godotenv.Read(envFileInConfigDir) if envFileErr != nil {