This commit is contained in:
parent
aee513f786
commit
5c2bfcc940
3
.changes/unreleased/Added-20250111-211546.yaml
Normal file
3
.changes/unreleased/Added-20250111-211546.yaml
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
kind: Added
|
||||||
|
body: '[feat]: package `packageOperation` option `checkVersion` implemented'
|
||||||
|
time: 2025-01-11T21:15:46.207199643-06:00
|
3
.changes/unreleased/Added-20250111-211813.yaml
Normal file
3
.changes/unreleased/Added-20250111-211813.yaml
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
kind: Added
|
||||||
|
body: user management added - see docs
|
||||||
|
time: 2025-01-11T21:18:13.182822019-06:00
|
3
.changes/unreleased/Added-20250113-231248.yaml
Normal file
3
.changes/unreleased/Added-20250113-231248.yaml
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
kind: Added
|
||||||
|
body: Support for remote config sources. Only config file and list can be used for now.
|
||||||
|
time: 2025-01-13T23:12:48.383700682-06:00
|
3
.changes/unreleased/Changed-20250113-231007.yaml
Normal file
3
.changes/unreleased/Changed-20250113-231007.yaml
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
kind: Changed
|
||||||
|
body: Internal refactoring of config setup
|
||||||
|
time: 2025-01-13T23:10:07.215735108-06:00
|
3
.changes/unreleased/Changed-20250113-231622.yaml
Normal file
3
.changes/unreleased/Changed-20250113-231622.yaml
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
kind: Changed
|
||||||
|
body: Formatting and sending for notifications
|
||||||
|
time: 2025-01-13T23:16:22.260458782-06:00
|
@ -1,3 +1,3 @@
|
|||||||
## v0.6.1 - 2025-01-04
|
## v0.6.1 - 2025-01-04
|
||||||
### Fixed
|
### Fixed
|
||||||
* Hooks now run explicitly after the command executes. Fixed panic due to improper logic.
|
* When running a list, hooks now run explicitly after the command executes. Fixed panic due to improper logic.
|
1
.vscode/settings.json
vendored
1
.vscode/settings.json
vendored
@ -1,6 +1,7 @@
|
|||||||
{
|
{
|
||||||
"cSpell.words": [
|
"cSpell.words": [
|
||||||
"Cmds",
|
"Cmds",
|
||||||
|
"configfetcher",
|
||||||
"knadh",
|
"knadh",
|
||||||
"koanf",
|
"koanf",
|
||||||
"mattn",
|
"mattn",
|
||||||
|
@ -31,8 +31,7 @@ func init() {
|
|||||||
func Backup(cmd *cobra.Command, args []string) {
|
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()
|
||||||
|
backyConfOpts.ReadConfig()
|
||||||
backy.ReadConfig(backyConfOpts)
|
|
||||||
|
|
||||||
backyConfOpts.RunListConfig("")
|
backyConfOpts.RunListConfig("")
|
||||||
for _, host := range backyConfOpts.Hosts {
|
for _, host := range backyConfOpts.Hosts {
|
||||||
|
@ -19,6 +19,7 @@ func cron(cmd *cobra.Command, args []string) {
|
|||||||
|
|
||||||
opts := backy.NewOpts(cfgFile, backy.CronEnabled())
|
opts := backy.NewOpts(cfgFile, backy.CronEnabled())
|
||||||
opts.InitConfig()
|
opts.InitConfig()
|
||||||
backy.ReadConfig(opts)
|
opts.ReadConfig()
|
||||||
|
|
||||||
opts.Cron()
|
opts.Cron()
|
||||||
}
|
}
|
||||||
|
@ -33,8 +33,8 @@ func execute(cmd *cobra.Command, args []string) {
|
|||||||
logging.ExitWithMSG("Please provide a command to run. Pass --help to see options.", 1, 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), backy.SetLogFile(logFile))
|
||||||
opts.InitConfig()
|
opts.InitConfig()
|
||||||
// opts.InitMongo()
|
opts.ReadConfig()
|
||||||
backy.ReadConfig(opts).ExecuteCmds(opts)
|
opts.ExecuteCmds()
|
||||||
}
|
}
|
||||||
|
@ -29,10 +29,10 @@ func init() {
|
|||||||
// 2. stdin (on command line) (TODO)
|
// 2. stdin (on command line) (TODO)
|
||||||
|
|
||||||
func Host(cmd *cobra.Command, args []string) {
|
func Host(cmd *cobra.Command, args []string) {
|
||||||
backyConfOpts := backy.NewOpts(cfgFile)
|
backyConfOpts := backy.NewOpts(cfgFile, backy.SetLogFile(logFile))
|
||||||
backyConfOpts.InitConfig()
|
backyConfOpts.InitConfig()
|
||||||
|
|
||||||
backy.ReadConfig(backyConfOpts)
|
backyConfOpts.ReadConfig()
|
||||||
|
|
||||||
// check CLI input
|
// check CLI input
|
||||||
if hostsList == nil {
|
if hostsList == nil {
|
||||||
|
@ -31,7 +31,7 @@ func init() {
|
|||||||
|
|
||||||
func List(cmd *cobra.Command, args []string) {
|
func List(cmd *cobra.Command, args []string) {
|
||||||
|
|
||||||
// settup based on whats passed in:
|
// setup based on whats passed in:
|
||||||
// - cmds
|
// - cmds
|
||||||
// - lists
|
// - lists
|
||||||
// - if none, list all commands
|
// - if none, list all commands
|
||||||
@ -42,8 +42,7 @@ func List(cmd *cobra.Command, args []string) {
|
|||||||
opts := backy.NewOpts(cfgFile)
|
opts := backy.NewOpts(cfgFile)
|
||||||
|
|
||||||
opts.InitConfig()
|
opts.InitConfig()
|
||||||
|
opts.ReadConfig()
|
||||||
opts = backy.ReadConfig(opts)
|
|
||||||
|
|
||||||
opts.ListCommand("rm-sn-db")
|
opts.ListCommand("rm-sn-db")
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,7 @@ var (
|
|||||||
// Used for flags.
|
// Used for flags.
|
||||||
cfgFile string
|
cfgFile string
|
||||||
verbose bool
|
verbose bool
|
||||||
|
logFile string
|
||||||
|
|
||||||
rootCmd = &cobra.Command{
|
rootCmd = &cobra.Command{
|
||||||
Use: "backy",
|
Use: "backy",
|
||||||
@ -33,6 +34,8 @@ func Execute() {
|
|||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
|
||||||
|
rootCmd.PersistentFlags().StringVar(&logFile, "log-file", "", "log file to write to")
|
||||||
|
|
||||||
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")
|
||||||
|
|
||||||
|
@ -28,9 +28,10 @@ commands:
|
|||||||
cmd: hostname
|
cmd: hostname
|
||||||
update-docker:
|
update-docker:
|
||||||
type: package
|
type: package
|
||||||
packageManager: apt
|
|
||||||
packageName: docker-ce
|
packageName: docker-ce
|
||||||
|
packageManager: apt
|
||||||
packageVersion: "5:27.4.1-1~debian.12~bookworm"
|
packageVersion: "5:27.4.1-1~debian.12~bookworm"
|
||||||
|
packageOperation: update
|
||||||
|
|
||||||
cmd-lists:
|
cmd-lists:
|
||||||
cmds-to-run: # this can be any name you want
|
cmds-to-run: # this can be any name you want
|
||||||
@ -67,7 +68,7 @@ hosts:
|
|||||||
# optional
|
# optional
|
||||||
logging:
|
logging:
|
||||||
verbose: true
|
verbose: true
|
||||||
file: /path/to/logs/commands.log
|
file: ./backy.log
|
||||||
console: false
|
console: false
|
||||||
cmd-std-out: false
|
cmd-std-out: false
|
||||||
|
|
||||||
|
27
go.mod
27
go.mod
@ -1,29 +1,52 @@
|
|||||||
module git.andrewnw.xyz/CyberShell/backy
|
module git.andrewnw.xyz/CyberShell/backy
|
||||||
|
|
||||||
go 1.20
|
go 1.21
|
||||||
|
|
||||||
|
toolchain go1.22.2
|
||||||
|
|
||||||
replace git.andrewnw.xyz/CyberShell/backy => /home/andrew/Projects/backy
|
replace git.andrewnw.xyz/CyberShell/backy => /home/andrew/Projects/backy
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/aws/aws-sdk-go-v2/config v1.18.27
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/s3 v1.72.0
|
||||||
github.com/go-co-op/gocron v1.33.1
|
github.com/go-co-op/gocron v1.33.1
|
||||||
github.com/hashicorp/vault/api v1.10.0
|
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/parsers/yaml v0.1.0
|
||||||
github.com/knadh/koanf/providers/file v0.1.0
|
github.com/knadh/koanf/providers/file v0.1.0
|
||||||
|
github.com/knadh/koanf/providers/rawbytes v0.1.0
|
||||||
github.com/knadh/koanf/v2 v2.0.1
|
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/sethvargo/go-password v0.3.1
|
||||||
github.com/spf13/cobra v1.7.0
|
github.com/spf13/cobra v1.7.0
|
||||||
golang.org/x/crypto v0.13.0
|
golang.org/x/crypto v0.13.0
|
||||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1
|
gopkg.in/natefinch/lumberjack.v2 v2.2.1
|
||||||
|
gopkg.in/yaml.v3 v3.0.1
|
||||||
maunium.net/go/mautrix v0.16.0
|
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/aws/aws-sdk-go-v2 v1.32.7 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.7 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/credentials v1.13.26 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.4 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.26 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.26 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.35 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.26 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.4.7 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.7 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.7 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/sso v1.12.12 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.12 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/sts v1.19.2 // indirect
|
||||||
|
github.com/aws/smithy-go v1.22.1 // indirect
|
||||||
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/fsnotify/fsnotify v1.6.0 // indirect
|
github.com/fsnotify/fsnotify v1.6.0 // indirect
|
||||||
@ -39,6 +62,7 @@ require (
|
|||||||
github.com/hashicorp/go-sockaddr v1.0.5 // 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/jmespath/go-jmespath v0.4.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/knadh/koanf/maps v0.1.1 // indirect
|
github.com/knadh/koanf/maps v0.1.1 // indirect
|
||||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||||
@ -64,6 +88,5 @@ require (
|
|||||||
golang.org/x/sys v0.12.0 // indirect
|
golang.org/x/sys v0.12.0 // indirect
|
||||||
golang.org/x/text v0.13.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/yaml.v3 v3.0.1 // indirect
|
|
||||||
maunium.net/go/maulogger/v2 v2.4.1 // indirect
|
maunium.net/go/maulogger/v2 v2.4.1 // indirect
|
||||||
)
|
)
|
||||||
|
59
go.sum
59
go.sum
@ -1,4 +1,45 @@
|
|||||||
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||||
|
github.com/aws/aws-sdk-go-v2 v1.18.1/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw=
|
||||||
|
github.com/aws/aws-sdk-go-v2 v1.32.7 h1:ky5o35oENWi0JYWUZkB7WYvVPP+bcRF5/Iq7JWSb5Rw=
|
||||||
|
github.com/aws/aws-sdk-go-v2 v1.32.7/go.mod h1:P5WJBrYqqbWVaOxgH0X/FYYD47/nooaPOZPlQdmiN2U=
|
||||||
|
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.7 h1:lL7IfaFzngfx0ZwUGOZdsFFnQ5uLvR0hWqqhyE7Q9M8=
|
||||||
|
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.7/go.mod h1:QraP0UcVlQJsmHfioCrveWOC1nbiWUl3ej08h4mXWoc=
|
||||||
|
github.com/aws/aws-sdk-go-v2/config v1.18.27 h1:Az9uLwmssTE6OGTpsFqOnaGpLnKDqNYOJzWuC6UAYzA=
|
||||||
|
github.com/aws/aws-sdk-go-v2/config v1.18.27/go.mod h1:0My+YgmkGxeqjXZb5BYme5pc4drjTnM+x1GJ3zv42Nw=
|
||||||
|
github.com/aws/aws-sdk-go-v2/credentials v1.13.26 h1:qmU+yhKmOCyujmuPY7tf5MxR/RKyZrOPO3V4DobiTUk=
|
||||||
|
github.com/aws/aws-sdk-go-v2/credentials v1.13.26/go.mod h1:GoXt2YC8jHUBbA4jr+W3JiemnIbkXOfxSXcisUsZ3os=
|
||||||
|
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.4 h1:LxK/bitrAr4lnh9LnIS6i7zWbCOdMsfzKFBI6LUCS0I=
|
||||||
|
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.4/go.mod h1:E1hLXN/BL2e6YizK1zFlYd8vsfi2GTjbjBazinMmeaM=
|
||||||
|
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.34/go.mod h1:wZpTEecJe0Btj3IYnDx/VlUzor9wm3fJHyvLpQF0VwY=
|
||||||
|
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.26 h1:I/5wmGMffY4happ8NOCuIUEWGUvvFp5NSeQcXl9RHcI=
|
||||||
|
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.26/go.mod h1:FR8f4turZtNy6baO0KJ5FJUmXH/cSkI9fOngs0yl6mA=
|
||||||
|
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.28/go.mod h1:7VRpKQQedkfIEXb4k52I7swUnZP0wohVajJMRn3vsUw=
|
||||||
|
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.26 h1:zXFLuEuMMUOvEARXFUVJdfqZ4bvvSgdGRq/ATcrQxzM=
|
||||||
|
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.26/go.mod h1:3o2Wpy0bogG1kyOPrgkXA8pgIfEEv0+m19O9D5+W8y8=
|
||||||
|
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.35 h1:LWA+3kDM8ly001vJ1X1waCuLJdtTl48gwkPKWy9sosI=
|
||||||
|
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.35/go.mod h1:0Eg1YjxE0Bhn56lx+SHJwCzhW+2JGtizsrx+lCqrfm0=
|
||||||
|
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.26 h1:GeNJsIFHB+WW5ap2Tec4K6dzcVTsRbsT1Lra46Hv9ME=
|
||||||
|
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.26/go.mod h1:zfgMpwHDXX2WGoG84xG2H+ZlPTkJUU4YUvx2svLQYWo=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1 h1:iXtILhvDxB6kPvEXgsDhGaZCSC6LQET5ZHSdJozeI0Y=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1/go.mod h1:9nu0fVANtYiAePIBh2/pFUSwtJ402hLnp854CNoDOeE=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.4.7 h1:tB4tNw83KcajNAzaIMhkhVI2Nt8fAZd5A5ro113FEMY=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.4.7/go.mod h1:lvpyBGkZ3tZ9iSsUIcC2EWp+0ywa7aK3BLT+FwZi+mQ=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.28/go.mod h1:jj7znCIg05jXlaGBlFMGP8+7UN3VtCkRBG2spnmRQkU=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.7 h1:8eUsivBQzZHqe/3FE+cqwfH+0p5Jo8PFM/QYQSmeZ+M=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.7/go.mod h1:kLPQvGUmxn/fqiCrDeohwG33bq2pQpGeY62yRO6Nrh0=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.7 h1:Hi0KGbrnr57bEHWM0bJ1QcBzxLrL/k2DHvGYhb8+W1w=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.7/go.mod h1:wKNgWgExdjjrm4qvfbTorkvocEstaoDl4WCvGfeCy9c=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/s3 v1.72.0 h1:SAfh4pNx5LuTafKKWR02Y+hL3A+3TX8cTKG1OIAJaBk=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/s3 v1.72.0/go.mod h1:r+xl5yzMk9083rMR+sJ5TYj9Tihvf/l1oxzZXDgGj2Q=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/sso v1.12.12 h1:nneMBM2p79PGWBQovYO/6Xnc2ryRMw3InnDJq1FHkSY=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/sso v1.12.12/go.mod h1:HuCOxYsF21eKrerARYO6HapNeh9GBNq7fius2AcwodY=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.12 h1:2qTR7IFk7/0IN/adSFhYu9Xthr0zVFTgBrmPldILn80=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.12/go.mod h1:E4VrHCPzmVB/KFXtqBGKb3c8zpbNBgKe3fisDNLAW5w=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/sts v1.19.2 h1:XFJ2Z6sNUUcAz9poj+245DMkrHE4h2j5I9/xD50RHfE=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/sts v1.19.2/go.mod h1:dp0yLPsLBOi++WTxzCjA/oZqi6NPIhoR+uF7GeMU9eg=
|
||||||
|
github.com/aws/smithy-go v1.13.5/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA=
|
||||||
|
github.com/aws/smithy-go v1.22.1 h1:/HPHZQ0g7f4eUeK6HKglFz8uwVfZKgoI25rb/J+dnro=
|
||||||
|
github.com/aws/smithy-go v1.22.1/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg=
|
||||||
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
||||||
github.com/cenkalti/backoff/v3 v3.2.2 h1:cfUAAO3yvKMYKPrvhDuHSwQnhZNk/RMHKdZqKTxfm6M=
|
github.com/cenkalti/backoff/v3 v3.2.2 h1:cfUAAO3yvKMYKPrvhDuHSwQnhZNk/RMHKdZqKTxfm6M=
|
||||||
github.com/cenkalti/backoff/v3 v3.2.2/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs=
|
github.com/cenkalti/backoff/v3 v3.2.2/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs=
|
||||||
@ -11,6 +52,7 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
|
|||||||
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
|
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
|
||||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||||
github.com/frankban/quicktest v1.14.5 h1:dfYrrRyLtiqT9GyKXgdh+k4inNeTvmGbuSgZ3lx3GhA=
|
github.com/frankban/quicktest v1.14.5 h1:dfYrrRyLtiqT9GyKXgdh+k4inNeTvmGbuSgZ3lx3GhA=
|
||||||
|
github.com/frankban/quicktest v1.14.5/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
||||||
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.33.1 h1:wjX+Dg6Ae29a/f9BSQjY1Rl+jflTpW9aDyMqseCj78c=
|
github.com/go-co-op/gocron v1.33.1 h1:wjX+Dg6Ae29a/f9BSQjY1Rl+jflTpW9aDyMqseCj78c=
|
||||||
@ -18,9 +60,12 @@ github.com/go-co-op/gocron v1.33.1/go.mod h1:NLi+bkm4rRSy1F8U7iacZOz0xPseMoIOnva
|
|||||||
github.com/go-jose/go-jose/v3 v3.0.0 h1:s6rrhirfEP/CGIoc6p+PZAeogN2SxKav6Wp7+dyMWVo=
|
github.com/go-jose/go-jose/v3 v3.0.0 h1:s6rrhirfEP/CGIoc6p+PZAeogN2SxKav6Wp7+dyMWVo=
|
||||||
github.com/go-jose/go-jose/v3 v3.0.0/go.mod h1:RNkWWRld676jZEYoV3+XK8L2ZnNSvIsxFMht0mSX+u8=
|
github.com/go-jose/go-jose/v3 v3.0.0/go.mod h1:RNkWWRld676jZEYoV3+XK8L2ZnNSvIsxFMht0mSX+u8=
|
||||||
github.com/go-test/deep v1.0.2 h1:onZX1rnHT3Wv6cqNgYyFOOlgVKJrksuCMCRvJStbMYw=
|
github.com/go-test/deep v1.0.2 h1:onZX1rnHT3Wv6cqNgYyFOOlgVKJrksuCMCRvJStbMYw=
|
||||||
|
github.com/go-test/deep v1.0.2/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
|
||||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||||
|
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4=
|
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/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||||
@ -30,6 +75,7 @@ github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9n
|
|||||||
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
|
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
|
||||||
github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
|
github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
|
||||||
github.com/hashicorp/go-hclog v0.16.2 h1:K4ev2ib4LdQETX5cSZBG0DVLk1jwGqSPXBjdah3veNs=
|
github.com/hashicorp/go-hclog v0.16.2 h1:K4ev2ib4LdQETX5cSZBG0DVLk1jwGqSPXBjdah3veNs=
|
||||||
|
github.com/hashicorp/go-hclog v0.16.2/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
|
||||||
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
|
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
|
||||||
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
|
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
|
||||||
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
|
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
|
||||||
@ -51,6 +97,10 @@ github.com/hashicorp/vault/api v1.10.0 h1:/US7sIjWN6Imp4o/Rj1Ce2Nr5bki/AXi9vAW3p
|
|||||||
github.com/hashicorp/vault/api v1.10.0/go.mod h1:jo5Y/ET+hNyz+JnKDt8XLAdKs+AM0G5W0Vp1IrFI8N8=
|
github.com/hashicorp/vault/api v1.10.0/go.mod h1:jo5Y/ET+hNyz+JnKDt8XLAdKs+AM0G5W0Vp1IrFI8N8=
|
||||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||||
|
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
|
||||||
|
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
|
||||||
|
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
|
||||||
|
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
|
||||||
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
||||||
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
||||||
github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible h1:jdpOPRN1zP63Td1hDQbZW73xKmzDvZHzVdNYxhnTMDA=
|
github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible h1:jdpOPRN1zP63Td1hDQbZW73xKmzDvZHzVdNYxhnTMDA=
|
||||||
@ -63,12 +113,15 @@ github.com/knadh/koanf/parsers/yaml v0.1.0 h1:ZZ8/iGfRLvKSaMEECEBPM1HQslrZADk8fP
|
|||||||
github.com/knadh/koanf/parsers/yaml v0.1.0/go.mod h1:cvbUDC7AL23pImuQP0oRw/hPuccrNBS2bps8asS0CwY=
|
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 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/file v0.1.0/go.mod h1:rjJ/nHQl64iYCtAW2QQnF0eSmDEX/YZ/eNFj5yR6BvA=
|
||||||
|
github.com/knadh/koanf/providers/rawbytes v0.1.0 h1:dpzgu2KO6uf6oCb4aP05KDmKmAmI51k5pe8RYKQ0qME=
|
||||||
|
github.com/knadh/koanf/providers/rawbytes v0.1.0/go.mod h1:mMTB1/IcJ/yE++A2iEZbY1MLygX7vttU+C+S/YmPu9c=
|
||||||
github.com/knadh/koanf/v2 v2.0.1 h1:1dYGITt1I23x8cfx8ZnldtezdyaZtfAuRtIFOiRzK7g=
|
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/knadh/koanf/v2 v2.0.1/go.mod h1:ZeiIlIDXTE7w1lMT6UVcNiRAS2/rCeLn/GdLNvY1Dus=
|
||||||
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=
|
||||||
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
|
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
|
||||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||||
|
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
@ -106,6 +159,7 @@ github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzG
|
|||||||
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||||
github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o=
|
github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o=
|
||||||
github.com/rogpeppe/go-internal v1.10.1-0.20230524175051-ec119421bb97 h1:3RPlVWzZ/PDqmVuf/FKHARG5EMid/tl7cv54Sw/QRVY=
|
github.com/rogpeppe/go-internal v1.10.1-0.20230524175051-ec119421bb97 h1:3RPlVWzZ/PDqmVuf/FKHARG5EMid/tl7cv54Sw/QRVY=
|
||||||
|
github.com/rogpeppe/go-internal v1.10.1-0.20230524175051-ec119421bb97/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
|
||||||
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
||||||
github.com/rs/zerolog v1.30.0 h1:SymVODrcRsaRaSInD9yQtKbtWqwsfoPcRff/oRXLj4c=
|
github.com/rs/zerolog v1.30.0 h1:SymVODrcRsaRaSInD9yQtKbtWqwsfoPcRff/oRXLj4c=
|
||||||
github.com/rs/zerolog v1.30.0/go.mod h1:/tk+P47gFdPXq4QYjvCmT5/Gsug2nagsFWBWhAiSi1w=
|
github.com/rs/zerolog v1.30.0/go.mod h1:/tk+P47gFdPXq4QYjvCmT5/Gsug2nagsFWBWhAiSi1w=
|
||||||
@ -113,6 +167,8 @@ github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQD
|
|||||||
github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
||||||
github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk=
|
github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk=
|
||||||
github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc=
|
github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc=
|
||||||
|
github.com/sethvargo/go-password v0.3.1 h1:WqrLTjo7X6AcVYfC6R7GtSyuUQR9hGyAj/f1PYQZCJU=
|
||||||
|
github.com/sethvargo/go-password v0.3.1/go.mod h1:rXofC1zT54N7R8K/h1WDUdkf9BOx5OptoxrMBcrXzvs=
|
||||||
github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I=
|
github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I=
|
||||||
github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0=
|
github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0=
|
||||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||||
@ -168,6 +224,7 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|||||||
golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o=
|
golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o=
|
||||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/term v0.12.0 h1:/ZfYdc3zq+q02Rv9vGqTeSItdzZTSNDmfTi0mBAuidU=
|
golang.org/x/term v0.12.0 h1:/ZfYdc3zq+q02Rv9vGqTeSItdzZTSNDmfTi0mBAuidU=
|
||||||
|
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
|
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/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||||
@ -181,6 +238,8 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EV
|
|||||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
|
gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
|
||||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
|
gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
|
||||||
|
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
|
||||||
|
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
@ -49,63 +49,56 @@ func (command *Command) RunCmd(cmdCtxLogger zerolog.Logger, opts *ConfigOpts) ([
|
|||||||
ArgsStr += fmt.Sprintf(" %s", v)
|
ArgsStr += fmt.Sprintf(" %s", v)
|
||||||
}
|
}
|
||||||
|
|
||||||
command = getPackageCommand(command)
|
command = getCommandType(command)
|
||||||
|
|
||||||
|
if command.Type == "user" {
|
||||||
|
if command.UserOperation == "password" {
|
||||||
|
cmdCtxLogger.Info().Str("password", command.UserPassword).Msg("user password to be updated")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var errSSH error
|
var errSSH error
|
||||||
// is host defined
|
// is host defined
|
||||||
if command.Host != nil {
|
if command.Host != nil {
|
||||||
|
print("host is defined")
|
||||||
outputArr, errSSH = command.RunCmdSSH(cmdCtxLogger, opts)
|
outputArr, errSSH = command.RunCmdSSH(cmdCtxLogger, opts)
|
||||||
if errSSH != nil {
|
if errSSH != nil {
|
||||||
return outputArr, errSSH
|
return outputArr, errSSH
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
|
// Handle package operations
|
||||||
|
if command.Type == "package" && command.PackageOperation == "checkVersion" {
|
||||||
|
cmdCtxLogger.Info().Str("package", command.PackageName).Msg("Checking package versions")
|
||||||
|
|
||||||
|
// Execute the package version command
|
||||||
|
cmd := exec.Command(command.Cmd, command.Args...)
|
||||||
|
cmdOutWriters = io.MultiWriter(&cmdOutBuf)
|
||||||
|
cmd.Stdout = cmdOutWriters
|
||||||
|
cmd.Stderr = cmdOutWriters
|
||||||
|
|
||||||
|
if err := cmd.Run(); err != nil {
|
||||||
|
return nil, fmt.Errorf("error running command %s policy: %w", ArgsStr, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return parsePackageVersion(cmdOutBuf.String(), cmdCtxLogger, command, cmdOutBuf)
|
||||||
|
}
|
||||||
|
|
||||||
|
var localCMD *exec.Cmd
|
||||||
var err error
|
var err error
|
||||||
if command.Shell != "" {
|
if command.Shell != "" {
|
||||||
cmdCtxLogger.Info().Str("Command", fmt.Sprintf("Running command %s on local machine in %s", command.Name, command.Shell)).Send()
|
cmdCtxLogger.Info().Str("Command", fmt.Sprintf("Running command %s on local machine in %s", command.Name, command.Shell)).Send()
|
||||||
|
|
||||||
ArgsStr = fmt.Sprintf("%s %s", command.Cmd, ArgsStr)
|
ArgsStr = fmt.Sprintf("%s %s", command.Cmd, ArgsStr)
|
||||||
|
|
||||||
localCMD := exec.Command(command.Shell, "-c", ArgsStr)
|
localCMD = exec.Command(command.Shell, "-c", ArgsStr)
|
||||||
|
|
||||||
if command.Dir != nil {
|
} else {
|
||||||
localCMD.Dir = *command.Dir
|
|
||||||
}
|
|
||||||
injectEnvIntoLocalCMD(envVars, localCMD, cmdCtxLogger)
|
|
||||||
|
|
||||||
cmdOutWriters = io.MultiWriter(&cmdOutBuf)
|
|
||||||
|
|
||||||
if IsCmdStdOutEnabled() {
|
|
||||||
cmdOutWriters = io.MultiWriter(os.Stdout, &cmdOutBuf)
|
|
||||||
}
|
|
||||||
|
|
||||||
localCMD.Stdout = cmdOutWriters
|
|
||||||
localCMD.Stderr = cmdOutWriters
|
|
||||||
|
|
||||||
err = localCMD.Run()
|
|
||||||
|
|
||||||
outScanner := bufio.NewScanner(&cmdOutBuf)
|
|
||||||
|
|
||||||
for outScanner.Scan() {
|
|
||||||
outMap := make(map[string]interface{})
|
|
||||||
outMap["cmd"] = command.Name
|
|
||||||
outMap["output"] = outScanner.Text()
|
|
||||||
if str, ok := outMap["output"].(string); ok {
|
|
||||||
outputArr = append(outputArr, str)
|
|
||||||
}
|
|
||||||
cmdCtxLogger.Info().Fields(outMap).Send()
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
cmdCtxLogger.Error().Err(fmt.Errorf("error when running cmd %s: %w", command.Name, err)).Send()
|
|
||||||
return outputArr, err
|
|
||||||
}
|
|
||||||
return outputArr, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
cmdCtxLogger.Info().Str("Command", fmt.Sprintf("Running command %s on local machine", command.Name)).Send()
|
cmdCtxLogger.Info().Str("Command", fmt.Sprintf("Running command %s on local machine", command.Name)).Send()
|
||||||
|
|
||||||
localCMD := exec.Command(command.Cmd, command.Args...)
|
localCMD = exec.Command(command.Cmd, command.Args...)
|
||||||
|
}
|
||||||
|
|
||||||
if command.Dir != nil {
|
if command.Dir != nil {
|
||||||
localCMD.Dir = *command.Dir
|
localCMD.Dir = *command.Dir
|
||||||
@ -134,7 +127,9 @@ func (command *Command) RunCmd(cmdCtxLogger zerolog.Logger, opts *ConfigOpts) ([
|
|||||||
if str, ok := outMap["output"].(string); ok {
|
if str, ok := outMap["output"].(string); ok {
|
||||||
outputArr = append(outputArr, str)
|
outputArr = append(outputArr, str)
|
||||||
}
|
}
|
||||||
|
// if command.GetOutput {
|
||||||
cmdCtxLogger.Info().Fields(outMap).Send()
|
cmdCtxLogger.Info().Fields(outMap).Send()
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cmdCtxLogger.Error().Err(fmt.Errorf("error when running cmd %s: %w", command.Name, err)).Send()
|
cmdCtxLogger.Error().Err(fmt.Errorf("error when running cmd %s: %w", command.Name, err)).Send()
|
||||||
@ -173,7 +168,7 @@ func cmdListWorker(msgTemps *msgTemplates, jobs <-chan *CmdList, results chan<-
|
|||||||
|
|
||||||
// Notify failure
|
// Notify failure
|
||||||
if list.NotifyConfig != nil {
|
if list.NotifyConfig != nil {
|
||||||
notifyError(cmdLogger, msgTemps, list, cmdsRan, outStructArr, runErr, cmdToRun, opts)
|
notifyError(cmdLogger, msgTemps, list, cmdsRan, outStructArr, runErr, cmdToRun)
|
||||||
}
|
}
|
||||||
hasError = true
|
hasError = true
|
||||||
break
|
break
|
||||||
@ -227,7 +222,7 @@ func cmdsRanContains(cmd string, cmdsRan []string) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Helper to notify errors
|
// Helper to notify errors
|
||||||
func notifyError(logger zerolog.Logger, templates *msgTemplates, list *CmdList, cmdsRan []string, outStructArr []outStruct, err error, cmd *Command, opts *ConfigOpts) {
|
func notifyError(logger zerolog.Logger, templates *msgTemplates, list *CmdList, cmdsRan []string, outStructArr []outStruct, err error, cmd *Command) {
|
||||||
errStruct := map[string]interface{}{
|
errStruct := map[string]interface{}{
|
||||||
"listName": list.Name,
|
"listName": list.Name,
|
||||||
"CmdsRan": cmdsRan,
|
"CmdsRan": cmdsRan,
|
||||||
@ -299,13 +294,7 @@ func (opts *ConfigOpts) RunListConfig(cron string) {
|
|||||||
opts.closeHostConnections()
|
opts.closeHostConnections()
|
||||||
}
|
}
|
||||||
|
|
||||||
type CmdResult struct {
|
func (opts *ConfigOpts) ExecuteCmds() {
|
||||||
CmdName string // Name of the command executed
|
|
||||||
ListName string // Name of the command list
|
|
||||||
Error error // Error encountered, if any
|
|
||||||
}
|
|
||||||
|
|
||||||
func (config *ConfigOpts) ExecuteCmds(opts *ConfigOpts) {
|
|
||||||
for _, cmd := range opts.executeCmds {
|
for _, cmd := range opts.executeCmds {
|
||||||
cmdToRun := opts.Cmds[cmd]
|
cmdToRun := opts.Cmds[cmd]
|
||||||
cmdLogger := cmdToRun.GenerateLogger(opts)
|
cmdLogger := cmdToRun.GenerateLogger(opts)
|
||||||
@ -425,3 +414,14 @@ func (opts *ConfigOpts) ExecCmdsSSH(cmdList []string, hostsList []string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// func executeUserCommands() []string {
|
||||||
|
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // parseRemoteSources parses source and validates fields using sourceType
|
||||||
|
// func (c *Command) parseRemoteSources(source, sourceType string) {
|
||||||
|
// switch sourceType {
|
||||||
|
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
@ -2,17 +2,19 @@ package backy
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"git.andrewnw.xyz/CyberShell/backy/pkg/configfetcher"
|
||||||
"git.andrewnw.xyz/CyberShell/backy/pkg/logging"
|
"git.andrewnw.xyz/CyberShell/backy/pkg/logging"
|
||||||
"git.andrewnw.xyz/CyberShell/backy/pkg/pkgman"
|
"git.andrewnw.xyz/CyberShell/backy/pkg/pkgman"
|
||||||
|
"git.andrewnw.xyz/CyberShell/backy/pkg/usermanager"
|
||||||
vault "github.com/hashicorp/vault/api"
|
vault "github.com/hashicorp/vault/api"
|
||||||
"github.com/knadh/koanf/parsers/yaml"
|
"github.com/knadh/koanf/parsers/yaml"
|
||||||
"github.com/knadh/koanf/providers/file"
|
"github.com/knadh/koanf/providers/rawbytes"
|
||||||
"github.com/knadh/koanf/v2"
|
"github.com/knadh/koanf/v2"
|
||||||
"github.com/mattn/go-isatty"
|
"github.com/mattn/go-isatty"
|
||||||
"github.com/rs/zerolog"
|
"github.com/rs/zerolog"
|
||||||
@ -26,64 +28,72 @@ var configFiles []string
|
|||||||
const macroStart string = "%{"
|
const macroStart string = "%{"
|
||||||
const macroEnd string = "}%"
|
const macroEnd string = "}%"
|
||||||
const envMacroStart string = "%{env:"
|
const envMacroStart string = "%{env:"
|
||||||
const vaultMacroStart string = "%{env:"
|
const vaultMacroStart string = "%{vault:"
|
||||||
|
|
||||||
func (opts *ConfigOpts) InitConfig() {
|
func (opts *ConfigOpts) InitConfig() {
|
||||||
|
homeDir, err := os.UserHomeDir()
|
||||||
homeDir, homeDirErr = os.UserHomeDir()
|
if err != nil {
|
||||||
|
logging.ExitWithMSG(err.Error(), 1, nil)
|
||||||
if homeDirErr != nil {
|
|
||||||
fmt.Println(homeDirErr)
|
|
||||||
logging.ExitWithMSG(homeDirErr.Error(), 1, nil)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
backyHomeConfDir = homeDir + "/.config/backy/"
|
backyHomeConfDir := path.Join(homeDir, ".config/backy/")
|
||||||
|
configFiles := []string{
|
||||||
configFiles = []string{"./backy.yml", "./backy.yaml", backyHomeConfDir + "backy.yml", backyHomeConfDir + "backy.yaml"}
|
"./backy.yml", "./backy.yaml",
|
||||||
|
path.Join(backyHomeConfDir, "backy.yml"),
|
||||||
|
path.Join(backyHomeConfDir, "backy.yaml"),
|
||||||
|
}
|
||||||
|
|
||||||
backyKoanf := koanf.New(".")
|
backyKoanf := koanf.New(".")
|
||||||
|
|
||||||
opts.ConfigFilePath = strings.TrimSpace(opts.ConfigFilePath)
|
opts.ConfigFilePath = strings.TrimSpace(opts.ConfigFilePath)
|
||||||
|
|
||||||
|
// Initialize the fetcher
|
||||||
|
fetcher := configfetcher.NewConfigFetcher(opts.ConfigFilePath)
|
||||||
|
|
||||||
if opts.ConfigFilePath != "" {
|
if opts.ConfigFilePath != "" {
|
||||||
err := testFile(opts.ConfigFilePath)
|
loadConfigFile(fetcher, opts.ConfigFilePath, backyKoanf, opts)
|
||||||
if err != nil {
|
|
||||||
logging.ExitWithMSG(fmt.Sprintf("Could not open config file %s: %v", opts.ConfigFilePath, err), 1, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
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 {
|
||||||
|
loadDefaultConfigFiles(fetcher, configFiles, backyKoanf, opts)
|
||||||
cFileFailures := 0
|
|
||||||
for _, c := range configFiles {
|
|
||||||
if err := backyKoanf.Load(file.Provider(c), yaml.Parser()); err != nil {
|
|
||||||
cFileFailures++
|
|
||||||
} else {
|
|
||||||
opts.ConfigFilePath = c
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if cFileFailures == 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
|
opts.koanf = backyKoanf
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReadConfig validates and reads the config file.
|
func loadConfigFile(fetcher configfetcher.ConfigFetcher, filePath string, k *koanf.Koanf, opts *ConfigOpts) {
|
||||||
func ReadConfig(opts *ConfigOpts) *ConfigOpts {
|
data, err := fetcher.Fetch(filePath)
|
||||||
|
if err != nil {
|
||||||
if isatty.IsTerminal(os.Stdout.Fd()) {
|
logging.ExitWithMSG(fmt.Sprintf("Could not fetch config file %s: %v", filePath, err), 1, nil)
|
||||||
os.Setenv("BACKY_TERM", "enabled")
|
|
||||||
} else if isatty.IsCygwinTerminal(os.Stdout.Fd()) {
|
|
||||||
os.Setenv("BACKY_TERM", "enabled")
|
|
||||||
} else {
|
|
||||||
os.Setenv("BACKY_TERM", "disabled")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := k.Load(rawbytes.Provider(data), yaml.Parser()); err != nil {
|
||||||
|
logging.ExitWithMSG(fmt.Sprintf("error loading config: %v", err), 1, &opts.Logger)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadDefaultConfigFiles(fetcher configfetcher.ConfigFetcher, configFiles []string, k *koanf.Koanf, opts *ConfigOpts) {
|
||||||
|
cFileFailures := 0
|
||||||
|
for _, c := range configFiles {
|
||||||
|
data, err := fetcher.Fetch(c)
|
||||||
|
if err != nil {
|
||||||
|
cFileFailures++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := k.Load(rawbytes.Provider(data), yaml.Parser()); err != nil {
|
||||||
|
cFileFailures++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if cFileFailures == len(configFiles) {
|
||||||
|
logging.ExitWithMSG("Could not find any valid config file", 1, nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (opts *ConfigOpts) ReadConfig() *ConfigOpts {
|
||||||
|
setTerminalEnv()
|
||||||
|
|
||||||
backyKoanf := opts.koanf
|
backyKoanf := opts.koanf
|
||||||
|
|
||||||
opts.loadEnv()
|
opts.loadEnv()
|
||||||
@ -94,228 +104,39 @@ func ReadConfig(opts *ConfigOpts) *ConfigOpts {
|
|||||||
|
|
||||||
CheckConfigValues(backyKoanf, opts.ConfigFilePath)
|
CheckConfigValues(backyKoanf, opts.ConfigFilePath)
|
||||||
|
|
||||||
// check for commands in file
|
validateCommands(backyKoanf, opts)
|
||||||
for _, c := range opts.executeCmds {
|
|
||||||
if !backyKoanf.Exists(getCmdFromConfig(c)) {
|
|
||||||
logging.ExitWithMSG(Sprintf("command %s is not in config file %s", c, opts.ConfigFilePath), 1, nil)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: refactor this further down the line
|
setLoggingOptions(backyKoanf, opts)
|
||||||
|
|
||||||
// for _, l := range opts.executeLists {
|
|
||||||
// if !backyKoanf.Exists(getCmdListFromConfig(l)) {
|
|
||||||
// logging.ExitWithMSG(Sprintf("list %s not found", l), 1, nil)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// check for verbosity, via
|
|
||||||
// 1. config file
|
|
||||||
// 2. TODO: CLI flag
|
|
||||||
// 3. TODO: ENV var
|
|
||||||
|
|
||||||
var (
|
|
||||||
isLoggingVerbose bool
|
|
||||||
logFile string
|
|
||||||
)
|
|
||||||
|
|
||||||
isLoggingVerbose = backyKoanf.Bool(getLoggingKeyFromConfig("verbose"))
|
|
||||||
|
|
||||||
logFile = fmt.Sprintf("%s/backy.log", path.Dir(opts.ConfigFilePath)) // get full path to logfile
|
|
||||||
|
|
||||||
if backyKoanf.Exists(getLoggingKeyFromConfig("file")) {
|
|
||||||
logFile = backyKoanf.String(getLoggingKeyFromConfig("file"))
|
|
||||||
}
|
|
||||||
|
|
||||||
zerolog.SetGlobalLevel(zerolog.InfoLevel)
|
|
||||||
|
|
||||||
if isLoggingVerbose {
|
|
||||||
zerolog.SetGlobalLevel(zerolog.DebugLevel)
|
|
||||||
globalLvl := zerolog.GlobalLevel()
|
|
||||||
os.Setenv("BACKY_LOGLEVEL", Sprintf("%v", globalLvl))
|
|
||||||
}
|
|
||||||
|
|
||||||
consoleLoggingDisabled := backyKoanf.Bool(getLoggingKeyFromConfig("console-disabled"))
|
|
||||||
|
|
||||||
os.Setenv("BACKY_CONSOLE_LOGGING", "enabled")
|
|
||||||
// Other qualifiers can go here as well
|
|
||||||
if consoleLoggingDisabled {
|
|
||||||
os.Setenv("BACKY_CONSOLE_LOGGING", "")
|
|
||||||
}
|
|
||||||
|
|
||||||
writers := logging.SetLoggingWriters(logFile)
|
|
||||||
|
|
||||||
log := zerolog.New(writers).With().Timestamp().Logger()
|
|
||||||
|
|
||||||
|
log := setupLogger(opts)
|
||||||
opts.Logger = log
|
opts.Logger = log
|
||||||
|
|
||||||
log.Info().Str("config file", opts.ConfigFilePath).Send()
|
log.Info().Str("config file", opts.ConfigFilePath).Send()
|
||||||
|
|
||||||
unmarshalErr := backyKoanf.UnmarshalWithConf("commands", &opts.Cmds, koanf.UnmarshalConf{Tag: "yaml"})
|
unmarshalConfig(backyKoanf, "commands", &opts.Cmds, opts.Logger)
|
||||||
|
|
||||||
if unmarshalErr != nil {
|
validateCommandEnvironments(opts)
|
||||||
|
|
||||||
panic(fmt.Errorf("error unmarshaling cmds struct: %w", unmarshalErr))
|
unmarshalConfig(backyKoanf, "hosts", &opts.Hosts, opts.Logger)
|
||||||
|
|
||||||
}
|
resolveHostConfigs(opts)
|
||||||
|
|
||||||
for cmdName, cmdConf := range opts.Cmds {
|
loadCommandLists(opts, backyKoanf)
|
||||||
envFileErr := testFile(cmdConf.Env)
|
|
||||||
if envFileErr != nil {
|
|
||||||
opts.Logger.Info().Str("cmd", cmdName).Err(envFileErr).Send()
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
expandEnvVars(opts.backyEnv, cmdConf.Environment)
|
validateCommandLists(opts)
|
||||||
}
|
|
||||||
|
|
||||||
// Get host configurations from config file
|
if opts.cronEnabled && len(opts.CmdConfigLists) == 0 {
|
||||||
|
|
||||||
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 opts.Hosts {
|
|
||||||
if host.Host == "" {
|
|
||||||
host.Host = hostConfigName
|
|
||||||
}
|
|
||||||
if host.ProxyJump != "" {
|
|
||||||
proxyHosts := strings.Split(host.ProxyJump, ",")
|
|
||||||
for hostNum, h := range proxyHosts {
|
|
||||||
if hostNum > 1 {
|
|
||||||
proxyHost, defined := opts.Hosts[h]
|
|
||||||
if defined {
|
|
||||||
host.ProxyHost = append(host.ProxyHost, proxyHost)
|
|
||||||
} else {
|
|
||||||
newProxy := &Host{Host: h}
|
|
||||||
host.ProxyHost = append(host.ProxyHost, newProxy)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
proxyHost, defined := opts.Hosts[h]
|
|
||||||
if defined {
|
|
||||||
host.ProxyHost = append(host.ProxyHost, proxyHost)
|
|
||||||
} else {
|
|
||||||
newHost := &Host{Host: h}
|
|
||||||
host.ProxyHost = append(host.ProxyHost, newHost)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// get command lists
|
|
||||||
// command lists should still be in the same file if no:
|
|
||||||
// 1. key 'cmd-lists.file' is found
|
|
||||||
// 2. hosts.yml or hosts.yaml is found in the same directory as the backy config file
|
|
||||||
backyConfigFileDir := path.Dir(opts.ConfigFilePath)
|
|
||||||
|
|
||||||
listsConfig := koanf.New(".")
|
|
||||||
|
|
||||||
listConfigFiles := []string{path.Join(backyConfigFileDir, "lists.yml"), path.Join(backyConfigFileDir, "lists.yaml")}
|
|
||||||
|
|
||||||
log.Info().Strs("list config files", listConfigFiles).Send()
|
|
||||||
for _, l := range listConfigFiles {
|
|
||||||
cFileFailures := 0
|
|
||||||
if err := listsConfig.Load(file.Provider(l), yaml.Parser()); err != nil {
|
|
||||||
cFileFailures++
|
|
||||||
} else {
|
|
||||||
opts.ConfigFilePath = l
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
if cFileFailures == len(configFiles) {
|
|
||||||
|
|
||||||
logging.ExitWithMSG(fmt.Sprintf("could not find a config file. Put one in the following paths: %v", listConfigFiles), 1, &opts.Logger)
|
|
||||||
|
|
||||||
// logging.ExitWithMSG((fmt.Sprintf("error unmarshalling cmd list struct: %v", unmarshalErr)), 1, &opts.Logger)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
_ = listsConfig.UnmarshalWithConf("cmd-lists", &opts.CmdConfigLists, koanf.UnmarshalConf{Tag: "yaml"})
|
|
||||||
|
|
||||||
if backyKoanf.Exists("cmd-lists") {
|
|
||||||
|
|
||||||
unmarshalErr = backyKoanf.UnmarshalWithConf("cmd-lists", &opts.CmdConfigLists, koanf.UnmarshalConf{Tag: "yaml"})
|
|
||||||
// if unmarshalErr is not nil, look for a cmd-lists.file key
|
|
||||||
if unmarshalErr != nil {
|
|
||||||
|
|
||||||
// if file key exists, resolve file path and try to read and unmarshal file into command lists config
|
|
||||||
if backyKoanf.Exists("cmd-lists.file") {
|
|
||||||
opts.CmdListFile = strings.TrimSpace(backyKoanf.String("cmd-lists.file"))
|
|
||||||
|
|
||||||
cmdListFilePath := path.Clean(opts.CmdListFile)
|
|
||||||
|
|
||||||
// if path is not absolute, check config directory
|
|
||||||
if !strings.HasPrefix(cmdListFilePath, "/") {
|
|
||||||
opts.CmdListFile = path.Join(backyConfigFileDir, cmdListFilePath)
|
|
||||||
}
|
|
||||||
|
|
||||||
err := testFile(opts.CmdListFile)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
logging.ExitWithMSG(fmt.Sprintf("Could not open config file %s: %v. \n\nThe cmd-lists config should be in the main config file or should be in a lists.yml or lists.yaml file.", opts.CmdListFile, err), 1, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := listsConfig.Load(file.Provider(opts.CmdListFile), yaml.Parser()); err != nil {
|
|
||||||
logging.ExitWithMSG(fmt.Sprintf("error loading config: %v", err), 1, &opts.Logger)
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Info().Str("using lists config file", opts.CmdListFile).Send()
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
var cmdNotFoundSliceErr []error
|
|
||||||
for cmdListName, cmdList := range opts.CmdConfigLists {
|
|
||||||
if opts.cronEnabled {
|
|
||||||
cron := strings.TrimSpace(cmdList.Cron)
|
|
||||||
if cron == "" {
|
|
||||||
delete(opts.CmdConfigLists, cmdListName)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for _, cmdInList := range cmdList.Order {
|
|
||||||
_, 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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Exit program if command is not found from list
|
|
||||||
if len(cmdNotFoundSliceErr) > 0 {
|
|
||||||
var cmdNotFoundErrorLog = log.Fatal()
|
|
||||||
cmdNotFoundErrorLog.Errs("commands not found", cmdNotFoundSliceErr).Send()
|
|
||||||
}
|
|
||||||
|
|
||||||
if opts.cronEnabled && (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)
|
||||||
}
|
}
|
||||||
|
|
||||||
// process commands
|
|
||||||
if err := processCmds(opts); err != nil {
|
if err := processCmds(opts); err != nil {
|
||||||
logging.ExitWithMSG(err.Error(), 1, &opts.Logger)
|
logging.ExitWithMSG(err.Error(), 1, &opts.Logger)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(opts.executeLists) > 0 {
|
filterExecuteLists(opts)
|
||||||
for l := range opts.CmdConfigLists {
|
|
||||||
if !contains(opts.executeLists, l) {
|
|
||||||
delete(opts.CmdConfigLists, l)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if backyKoanf.Exists("notifications") {
|
if backyKoanf.Exists("notifications") {
|
||||||
|
unmarshalConfig(backyKoanf, "notifications", &opts.NotificationConf, opts.Logger)
|
||||||
unmarshalErr = backyKoanf.UnmarshalWithConf("notifications", &opts.NotificationConf, koanf.UnmarshalConf{Tag: "yaml"})
|
|
||||||
if unmarshalErr != nil {
|
|
||||||
fmt.Printf("error unmarshalling notifications object: %v", unmarshalErr)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
opts.SetupNotify()
|
opts.SetupNotify()
|
||||||
@ -327,6 +148,176 @@ func ReadConfig(opts *ConfigOpts) *ConfigOpts {
|
|||||||
return opts
|
return opts
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func setTerminalEnv() {
|
||||||
|
if isatty.IsTerminal(os.Stdout.Fd()) || isatty.IsCygwinTerminal(os.Stdout.Fd()) {
|
||||||
|
os.Setenv("BACKY_TERM", "enabled")
|
||||||
|
} else {
|
||||||
|
os.Setenv("BACKY_TERM", "disabled")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateCommands(k *koanf.Koanf, opts *ConfigOpts) {
|
||||||
|
for _, c := range opts.executeCmds {
|
||||||
|
if !k.Exists(getCmdFromConfig(c)) {
|
||||||
|
logging.ExitWithMSG(fmt.Sprintf("command %s is not in config file %s", c, opts.ConfigFilePath), 1, nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func setLoggingOptions(k *koanf.Koanf, opts *ConfigOpts) {
|
||||||
|
isLoggingVerbose := k.Bool(getLoggingKeyFromConfig("verbose"))
|
||||||
|
|
||||||
|
// if log file is set in config file and not set on command line, use "./backy.log"
|
||||||
|
logFile := "./backy.log"
|
||||||
|
if opts.LogFilePath == "" && k.Exists(getLoggingKeyFromConfig("file")) {
|
||||||
|
logFile = k.String(getLoggingKeyFromConfig("file"))
|
||||||
|
opts.LogFilePath = logFile
|
||||||
|
}
|
||||||
|
|
||||||
|
zerolog.SetGlobalLevel(zerolog.InfoLevel)
|
||||||
|
if isLoggingVerbose {
|
||||||
|
zerolog.SetGlobalLevel(zerolog.DebugLevel)
|
||||||
|
os.Setenv("BACKY_LOGLEVEL", fmt.Sprintf("%v", zerolog.GlobalLevel()))
|
||||||
|
}
|
||||||
|
|
||||||
|
if k.Bool(getLoggingKeyFromConfig("console-disabled")) {
|
||||||
|
os.Setenv("BACKY_CONSOLE_LOGGING", "")
|
||||||
|
} else {
|
||||||
|
os.Setenv("BACKY_CONSOLE_LOGGING", "enabled")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func setupLogger(opts *ConfigOpts) zerolog.Logger {
|
||||||
|
writers := logging.SetLoggingWriters(opts.LogFilePath)
|
||||||
|
return zerolog.New(writers).With().Timestamp().Logger()
|
||||||
|
}
|
||||||
|
|
||||||
|
func unmarshalConfig(k *koanf.Koanf, key string, target interface{}, log zerolog.Logger) {
|
||||||
|
if err := k.UnmarshalWithConf(key, target, koanf.UnmarshalConf{Tag: "yaml"}); err != nil {
|
||||||
|
logging.ExitWithMSG(fmt.Sprintf("error unmarshalling %s struct: %v", key, err), 1, &log)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateCommandEnvironments(opts *ConfigOpts) {
|
||||||
|
for cmdName, cmdConf := range opts.Cmds {
|
||||||
|
if err := testFile(cmdConf.Env); err != nil {
|
||||||
|
opts.Logger.Info().Str("cmd", cmdName).Err(err).Send()
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
expandEnvVars(opts.backyEnv, cmdConf.Environment)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func resolveHostConfigs(opts *ConfigOpts) {
|
||||||
|
for hostConfigName, host := range opts.Hosts {
|
||||||
|
if host.Host == "" {
|
||||||
|
host.Host = hostConfigName
|
||||||
|
}
|
||||||
|
if host.ProxyJump != "" {
|
||||||
|
resolveProxyHosts(host, opts)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func resolveProxyHosts(host *Host, opts *ConfigOpts) {
|
||||||
|
proxyHosts := strings.Split(host.ProxyJump, ",")
|
||||||
|
for _, h := range proxyHosts {
|
||||||
|
proxyHost, defined := opts.Hosts[h]
|
||||||
|
if !defined {
|
||||||
|
proxyHost = &Host{Host: h}
|
||||||
|
opts.Hosts[h] = proxyHost
|
||||||
|
}
|
||||||
|
host.ProxyHost = append(host.ProxyHost, proxyHost)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadCommandLists(opts *ConfigOpts, backyKoanf *koanf.Koanf) {
|
||||||
|
backyConfigFileDir := path.Dir(opts.ConfigFilePath)
|
||||||
|
listsConfig := koanf.New(".")
|
||||||
|
listConfigFiles := []string{
|
||||||
|
path.Join(backyConfigFileDir, "lists.yml"),
|
||||||
|
path.Join(backyConfigFileDir, "lists.yaml"),
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, l := range listConfigFiles {
|
||||||
|
if loadListConfigFile(l, listsConfig, opts) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if backyKoanf.Exists("cmd-lists") {
|
||||||
|
unmarshalConfig(backyKoanf, "cmd-lists", &opts.CmdConfigLists, opts.Logger)
|
||||||
|
if backyKoanf.Exists("cmd-lists.file") {
|
||||||
|
loadCmdListsFile(backyKoanf, listsConfig, opts)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadListConfigFile(filePath string, k *koanf.Koanf, opts *ConfigOpts) bool {
|
||||||
|
fetcher := configfetcher.NewConfigFetcher(filePath)
|
||||||
|
|
||||||
|
data, err := fetcher.Fetch(filePath)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := k.Load(rawbytes.Provider(data), yaml.Parser()); err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
opts.CmdListFile = filePath
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadCmdListsFile(backyKoanf *koanf.Koanf, listsConfig *koanf.Koanf, opts *ConfigOpts) {
|
||||||
|
opts.CmdListFile = strings.TrimSpace(backyKoanf.String("cmd-lists.file"))
|
||||||
|
if !path.IsAbs(opts.CmdListFile) {
|
||||||
|
opts.CmdListFile = path.Join(path.Dir(opts.ConfigFilePath), opts.CmdListFile)
|
||||||
|
}
|
||||||
|
|
||||||
|
fetcher := configfetcher.NewConfigFetcher(opts.CmdListFile)
|
||||||
|
|
||||||
|
data, err := fetcher.Fetch(opts.CmdListFile)
|
||||||
|
if err != nil {
|
||||||
|
logging.ExitWithMSG(fmt.Sprintf("Could not fetch config file %s: %v", opts.CmdListFile, err), 1, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := listsConfig.Load(rawbytes.Provider(data), yaml.Parser()); err != nil {
|
||||||
|
logging.ExitWithMSG(fmt.Sprintf("error loading config: %v", err), 1, &opts.Logger)
|
||||||
|
}
|
||||||
|
|
||||||
|
unmarshalConfig(listsConfig, "cmd-lists", &opts.CmdConfigLists, opts.Logger)
|
||||||
|
opts.Logger.Info().Str("using lists config file", opts.CmdListFile).Send()
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateCommandLists(opts *ConfigOpts) {
|
||||||
|
var cmdNotFoundSliceErr []error
|
||||||
|
for cmdListName, cmdList := range opts.CmdConfigLists {
|
||||||
|
if opts.cronEnabled && strings.TrimSpace(cmdList.Cron) == "" {
|
||||||
|
delete(opts.CmdConfigLists, cmdListName)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for _, cmdInList := range cmdList.Order {
|
||||||
|
if _, cmdNameFound := opts.Cmds[cmdInList]; !cmdNameFound {
|
||||||
|
cmdNotFoundSliceErr = append(cmdNotFoundSliceErr, fmt.Errorf("command %s in list %s is not defined in commands section in config file", cmdInList, cmdListName))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(cmdNotFoundSliceErr) > 0 {
|
||||||
|
opts.Logger.Fatal().Errs("commands not found", cmdNotFoundSliceErr).Send()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func filterExecuteLists(opts *ConfigOpts) {
|
||||||
|
if len(opts.executeLists) > 0 {
|
||||||
|
for l := range opts.CmdConfigLists {
|
||||||
|
if !contains(opts.executeLists, l) {
|
||||||
|
delete(opts.CmdConfigLists, l)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
func getNestedConfig(nestedConfig, key string) string {
|
func getNestedConfig(nestedConfig, key string) string {
|
||||||
return fmt.Sprintf("%s.%s", nestedConfig, key)
|
return fmt.Sprintf("%s.%s", nestedConfig, key)
|
||||||
}
|
}
|
||||||
@ -448,6 +439,7 @@ func GetVaultKey(str string, opts *ConfigOpts, log zerolog.Logger) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func processCmds(opts *ConfigOpts) error {
|
func processCmds(opts *ConfigOpts) error {
|
||||||
|
|
||||||
// process commands
|
// process commands
|
||||||
for cmdName, cmd := range opts.Cmds {
|
for cmdName, cmd := range opts.Cmds {
|
||||||
|
|
||||||
@ -503,7 +495,7 @@ func processCmds(opts *ConfigOpts) error {
|
|||||||
|
|
||||||
// Validate the operation
|
// Validate the operation
|
||||||
switch cmd.PackageOperation {
|
switch cmd.PackageOperation {
|
||||||
case "install", "remove", "upgrade":
|
case "install", "remove", "upgrade", "checkVersion":
|
||||||
cmd.pkgMan, err = pkgman.PackageManagerFactory(cmd.PackageManager, pkgman.WithoutAuth())
|
cmd.pkgMan, err = pkgman.PackageManagerFactory(cmd.PackageManager, pkgman.WithoutAuth())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -511,6 +503,33 @@ func processCmds(opts *ConfigOpts) error {
|
|||||||
default:
|
default:
|
||||||
return fmt.Errorf("unsupported package operation %s for command %s", cmd.PackageOperation, cmd.Name)
|
return fmt.Errorf("unsupported package operation %s for command %s", cmd.PackageOperation, cmd.Name)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse user commands
|
||||||
|
if cmd.Type == "user" {
|
||||||
|
if cmd.Username == "" {
|
||||||
|
return fmt.Errorf("username is required for user command %s", cmd.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
detectOSType(cmd, opts)
|
||||||
|
var err error
|
||||||
|
|
||||||
|
// Validate the operation
|
||||||
|
switch cmd.UserOperation {
|
||||||
|
case "add", "remove", "modify", "checkIfExists", "delete", "password":
|
||||||
|
cmd.userMan, err = usermanager.NewUserManager(cmd.OS)
|
||||||
|
if cmd.Host != nil {
|
||||||
|
host, ok := opts.Hosts[*cmd.Host]
|
||||||
|
if ok {
|
||||||
|
cmd.userMan, err = usermanager.NewUserManager(host.OS)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("unsupported user operation %s for command %s", cmd.UserOperation, cmd.Name)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -557,3 +576,32 @@ func processHooks(cmd *Command, hooks []string, opts *ConfigOpts, hookType strin
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func detectOSType(cmd *Command, opts *ConfigOpts) error {
|
||||||
|
if cmd.Host == nil {
|
||||||
|
if runtime.GOOS == "linux" { // also can be specified to FreeBSD
|
||||||
|
cmd.OS = "linux"
|
||||||
|
opts.Logger.Info().Msg("Unix/Linux type OS detected")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
host, ok := opts.Hosts[*cmd.Host]
|
||||||
|
if ok {
|
||||||
|
if host.OS != "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
os, err := host.DetectOS(opts)
|
||||||
|
os = strings.TrimSpace(os)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if os == "" {
|
||||||
|
return fmt.Errorf("error detecting os for command %s: empty string", cmd.Name)
|
||||||
|
}
|
||||||
|
if strings.Contains(os, "linux") {
|
||||||
|
os = "linux"
|
||||||
|
}
|
||||||
|
host.OS = os
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
117
pkg/backy/ssh.go
117
pkg/backy/ssh.go
@ -119,7 +119,7 @@ func (remoteConfig *Host) ConnectToHost(opts *ConfigOpts) error {
|
|||||||
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.Logger.Info().Str("user", remoteConfig.ClientConfig.User).Send()
|
// opts.Logger.Info().Str("user", remoteConfig.ClientConfig.User).Send()
|
||||||
|
|
||||||
remoteConfig.SshClient, connectErr = remoteConfig.ConnectThroughBastion(opts.Logger)
|
remoteConfig.SshClient, connectErr = remoteConfig.ConnectThroughBastion(opts.Logger)
|
||||||
if connectErr != nil {
|
if connectErr != nil {
|
||||||
@ -494,7 +494,7 @@ func (command *Command) RunCmdSSH(cmdCtxLogger zerolog.Logger, opts *ConfigOpts)
|
|||||||
)
|
)
|
||||||
|
|
||||||
command.Type = strings.TrimSpace(command.Type)
|
command.Type = strings.TrimSpace(command.Type)
|
||||||
command = getPackageCommand(command)
|
command = getCommandType(command)
|
||||||
|
|
||||||
// Prepare command arguments
|
// Prepare command arguments
|
||||||
for _, v := range command.Args {
|
for _, v := range command.Args {
|
||||||
@ -516,7 +516,7 @@ func (command *Command) RunCmdSSH(cmdCtxLogger zerolog.Logger, opts *ConfigOpts)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create new SSH session
|
// Create new SSH session
|
||||||
commandSession, err := command.createSSHSession(opts)
|
commandSession, err := command.RemoteHost.createSSHSession(opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to create SSH session: %w", err)
|
return nil, fmt.Errorf("failed to create SSH session: %w", err)
|
||||||
}
|
}
|
||||||
@ -539,6 +539,27 @@ func (command *Command) RunCmdSSH(cmdCtxLogger zerolog.Logger, opts *ConfigOpts)
|
|||||||
return command.runScript(commandSession, cmdCtxLogger, &cmdOutBuf)
|
return command.runScript(commandSession, cmdCtxLogger, &cmdOutBuf)
|
||||||
case "scriptFile":
|
case "scriptFile":
|
||||||
return command.runScriptFile(commandSession, cmdCtxLogger, &cmdOutBuf)
|
return command.runScriptFile(commandSession, cmdCtxLogger, &cmdOutBuf)
|
||||||
|
case "package":
|
||||||
|
if command.PackageOperation == "checkVersion" {
|
||||||
|
commandSession.Stderr = nil
|
||||||
|
// Execute the package version command remotely
|
||||||
|
// Parse the output of package version command
|
||||||
|
// Compare versions
|
||||||
|
// Check if a specific version is specified
|
||||||
|
commandSession.Stdout = nil
|
||||||
|
return checkPackageVersion(cmdCtxLogger, command, commandSession, cmdOutBuf)
|
||||||
|
} else {
|
||||||
|
if command.Shell != "" {
|
||||||
|
ArgsStr = fmt.Sprintf("%s -c '%s %s'", command.Shell, command.Cmd, ArgsStr)
|
||||||
|
} else {
|
||||||
|
ArgsStr = fmt.Sprintf("%s %s", command.Cmd, ArgsStr)
|
||||||
|
}
|
||||||
|
cmdCtxLogger.Debug().Str("cmd + args", ArgsStr).Send()
|
||||||
|
// Run simple command
|
||||||
|
if err := commandSession.Run(ArgsStr); err != nil {
|
||||||
|
return collectOutput(&cmdOutBuf, command.Name, cmdCtxLogger, true), fmt.Errorf("error running command: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
if command.Shell != "" {
|
if command.Shell != "" {
|
||||||
ArgsStr = fmt.Sprintf("%s -c '%s %s'", command.Shell, command.Cmd, ArgsStr)
|
ArgsStr = fmt.Sprintf("%s -c '%s %s'", command.Shell, command.Cmd, ArgsStr)
|
||||||
@ -548,11 +569,35 @@ func (command *Command) RunCmdSSH(cmdCtxLogger zerolog.Logger, opts *ConfigOpts)
|
|||||||
cmdCtxLogger.Debug().Str("cmd + args", ArgsStr).Send()
|
cmdCtxLogger.Debug().Str("cmd + args", ArgsStr).Send()
|
||||||
// Run simple command
|
// Run simple command
|
||||||
if err := commandSession.Run(ArgsStr); err != nil {
|
if err := commandSession.Run(ArgsStr); err != nil {
|
||||||
return collectOutput(&cmdOutBuf, command.Name, cmdCtxLogger), fmt.Errorf("error running command: %w", err)
|
return collectOutput(&cmdOutBuf, command.Name, cmdCtxLogger, command.GetOutput), fmt.Errorf("error running command: %w", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return collectOutput(&cmdOutBuf, command.Name, cmdCtxLogger), nil
|
return collectOutput(&cmdOutBuf, command.Name, cmdCtxLogger, true), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkPackageVersion(cmdCtxLogger zerolog.Logger, command *Command, commandSession *ssh.Session, cmdOutBuf bytes.Buffer) ([]string, error) {
|
||||||
|
cmdCtxLogger.Info().Str("package", command.PackageName).Msg("Checking package versions")
|
||||||
|
// Prepare command arguments
|
||||||
|
ArgsStr := command.Cmd
|
||||||
|
for _, v := range command.Args {
|
||||||
|
ArgsStr += fmt.Sprintf(" %s", v)
|
||||||
|
}
|
||||||
|
|
||||||
|
var err error
|
||||||
|
var cmdOut []byte
|
||||||
|
|
||||||
|
if cmdOut, err = commandSession.CombinedOutput(ArgsStr); err != nil {
|
||||||
|
cmdOutBuf.Write(cmdOut)
|
||||||
|
|
||||||
|
_, parseErr := parsePackageVersion(string(cmdOut), cmdCtxLogger, command, cmdOutBuf)
|
||||||
|
if parseErr != nil {
|
||||||
|
return collectOutput(&cmdOutBuf, command.Name, cmdCtxLogger, command.GetOutput), fmt.Errorf("error: package %s not listed: %w", command.PackageName, err)
|
||||||
|
}
|
||||||
|
return collectOutput(&cmdOutBuf, command.Name, cmdCtxLogger, command.GetOutput), fmt.Errorf("error running %s: %w", ArgsStr, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return parsePackageVersion(string(cmdOut), cmdCtxLogger, command, cmdOutBuf)
|
||||||
}
|
}
|
||||||
|
|
||||||
// getCommandTypeLabel returns a human-readable label for the command type.
|
// getCommandTypeLabel returns a human-readable label for the command type.
|
||||||
@ -563,20 +608,6 @@ func getCommandTypeLabel(commandType string) string {
|
|||||||
return fmt.Sprintf("%s command", commandType)
|
return fmt.Sprintf("%s command", commandType)
|
||||||
}
|
}
|
||||||
|
|
||||||
// createSSHSession attempts to create a new SSH session and retries on failure.
|
|
||||||
func (command *Command) createSSHSession(opts *ConfigOpts) (*ssh.Session, error) {
|
|
||||||
session, err := command.RemoteHost.SshClient.NewSession()
|
|
||||||
if err == nil {
|
|
||||||
return session, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Retry connection and session creation
|
|
||||||
if connErr := command.RemoteHost.ConnectToHost(opts); connErr != nil {
|
|
||||||
return nil, fmt.Errorf("session creation failed: %v, connection retry failed: %v", err, connErr)
|
|
||||||
}
|
|
||||||
return command.RemoteHost.SshClient.NewSession()
|
|
||||||
}
|
|
||||||
|
|
||||||
// runScript handles the execution of inline scripts.
|
// runScript handles the execution of inline scripts.
|
||||||
func (command *Command) runScript(session *ssh.Session, cmdCtxLogger zerolog.Logger, outputBuf *bytes.Buffer) ([]string, error) {
|
func (command *Command) runScript(session *ssh.Session, cmdCtxLogger zerolog.Logger, outputBuf *bytes.Buffer) ([]string, error) {
|
||||||
script, err := command.prepareScriptBuffer()
|
script, err := command.prepareScriptBuffer()
|
||||||
@ -590,10 +621,10 @@ func (command *Command) runScript(session *ssh.Session, cmdCtxLogger zerolog.Log
|
|||||||
}
|
}
|
||||||
|
|
||||||
if err := session.Wait(); err != nil {
|
if err := session.Wait(); err != nil {
|
||||||
return collectOutput(outputBuf, command.Name, cmdCtxLogger), fmt.Errorf("error waiting for shell: %w", err)
|
return collectOutput(outputBuf, command.Name, cmdCtxLogger, true), fmt.Errorf("error waiting for shell: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return collectOutput(outputBuf, command.Name, cmdCtxLogger), nil
|
return collectOutput(outputBuf, command.Name, cmdCtxLogger, command.GetOutput), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// runScriptFile handles the execution of script files.
|
// runScriptFile handles the execution of script files.
|
||||||
@ -609,10 +640,10 @@ func (command *Command) runScriptFile(session *ssh.Session, cmdCtxLogger zerolog
|
|||||||
}
|
}
|
||||||
|
|
||||||
if err := session.Wait(); err != nil {
|
if err := session.Wait(); err != nil {
|
||||||
return collectOutput(outputBuf, command.Name, cmdCtxLogger), fmt.Errorf("error waiting for shell: %w", err)
|
return collectOutput(outputBuf, command.Name, cmdCtxLogger, true), fmt.Errorf("error waiting for shell: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return collectOutput(outputBuf, command.Name, cmdCtxLogger), nil
|
return collectOutput(outputBuf, command.Name, cmdCtxLogger, true), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// prepareScriptBuffer prepares a buffer for inline scripts.
|
// prepareScriptBuffer prepares a buffer for inline scripts.
|
||||||
@ -677,13 +708,51 @@ func readFileToBuffer(filePath string) (*bytes.Buffer, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// collectOutput collects output from a buffer and logs it.
|
// collectOutput collects output from a buffer and logs it.
|
||||||
func collectOutput(buf *bytes.Buffer, commandName string, logger zerolog.Logger) []string {
|
func collectOutput(buf *bytes.Buffer, commandName string, logger zerolog.Logger, wantOutput bool) []string {
|
||||||
var outputArr []string
|
var outputArr []string
|
||||||
scanner := bufio.NewScanner(buf)
|
scanner := bufio.NewScanner(buf)
|
||||||
for scanner.Scan() {
|
for scanner.Scan() {
|
||||||
line := scanner.Text()
|
line := scanner.Text()
|
||||||
outputArr = append(outputArr, line)
|
outputArr = append(outputArr, line)
|
||||||
|
if wantOutput {
|
||||||
logger.Info().Str("cmd", commandName).Str("output", line).Send()
|
logger.Info().Str("cmd", commandName).Str("output", line).Send()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return outputArr
|
return outputArr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// createSSHSession attempts to create a new SSH session and retries on failure.
|
||||||
|
func (h *Host) createSSHSession(opts *ConfigOpts) (*ssh.Session, error) {
|
||||||
|
session, err := h.SshClient.NewSession()
|
||||||
|
if err == nil {
|
||||||
|
return session, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retry connection and session creation
|
||||||
|
if connErr := h.ConnectToHost(opts); connErr != nil {
|
||||||
|
return nil, fmt.Errorf("session creation failed: %v, connection retry failed: %v", err, connErr)
|
||||||
|
}
|
||||||
|
return h.SshClient.NewSession()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Host) DetectOS(opts *ConfigOpts) (string, error) {
|
||||||
|
err := h.ConnectToHost(opts)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
var session *ssh.Session
|
||||||
|
session, err = h.createSSHSession(opts)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
// Execute the "uname -a" command on the remote machine
|
||||||
|
output, err := session.CombinedOutput("uname")
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to execute OS detection command: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse the output to determine the OS
|
||||||
|
osName := string(output)
|
||||||
|
return osName, nil
|
||||||
|
}
|
||||||
|
@ -15,7 +15,7 @@ The following commands ran:
|
|||||||
{{end}}
|
{{end}}
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
||||||
{{ if .CmdOutput }}{{- range .CmdOutput }}Command output for {{ .CmdName }}:
|
{{ if .CmdOutput }}{{- range .CmdOutput }}{{ printf "\n"}}Command output for {{ .CmdName }}:
|
||||||
{{- range .Output}}
|
{{- range .Output}}
|
||||||
{{ . }}
|
{{ . }}
|
||||||
{{ end }}{{ end }}
|
{{ end }}{{ end }}
|
||||||
|
@ -5,7 +5,7 @@ The following commands ran:
|
|||||||
- {{. -}}
|
- {{. -}}
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
{{ if .CmdOutput }}{{- range .CmdOutput }}Command output for {{ .CmdName }}:
|
{{ if .CmdOutput }}{{- range .CmdOutput }}{{ printf "\n"}}Command output for {{ .CmdName }}:
|
||||||
{{- range .Output}}
|
{{- range .Output}}
|
||||||
{{ . }}
|
{{ . }}
|
||||||
{{ end }}{{ end }}
|
{{ end }}{{ end }}
|
||||||
|
@ -4,7 +4,10 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"text/template"
|
"text/template"
|
||||||
|
|
||||||
|
"strings"
|
||||||
|
|
||||||
"git.andrewnw.xyz/CyberShell/backy/pkg/pkgman"
|
"git.andrewnw.xyz/CyberShell/backy/pkg/pkgman"
|
||||||
|
"git.andrewnw.xyz/CyberShell/backy/pkg/usermanager"
|
||||||
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/knadh/koanf/v2"
|
||||||
@ -18,6 +21,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 {
|
||||||
|
OS string `yaml:"OS,omitempty"`
|
||||||
ConfigFilePath string `yaml:"config,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"`
|
||||||
@ -114,20 +118,32 @@ type (
|
|||||||
// Username specifies the username for user creation or related operations
|
// Username specifies the username for user creation or related operations
|
||||||
Username string `yaml:"username,omitempty"`
|
Username string `yaml:"username,omitempty"`
|
||||||
|
|
||||||
// Groups specifies the groups to add the user to
|
// UserGroups specifies the groups to add the user to
|
||||||
Groups []string `yaml:"groups,omitempty"`
|
UserGroups []string `yaml:"userGroups,omitempty"`
|
||||||
|
|
||||||
// Home specifies the home directory for the user
|
// UserHome specifies the home directory for the user
|
||||||
Home string `yaml:"home,omitempty"`
|
UserHome string `yaml:"userHome,omitempty"`
|
||||||
|
|
||||||
// System specifies whether the user is a system account
|
// UserShell specifies the shell for the user
|
||||||
System bool `yaml:"system,omitempty"`
|
UserShell string `yaml:"userShell,omitempty"`
|
||||||
|
|
||||||
// Password specifies the password for the user (can be file: or plain text)
|
// SystemUser specifies whether the user is a system account
|
||||||
Password string `yaml:"password,omitempty"`
|
SystemUser bool `yaml:"systemUser,omitempty"`
|
||||||
|
|
||||||
// Operation specifies the action for user-related commands (e.g., "create" or "remove")
|
// UserPassword specifies the password for the user (can be file: or plain text)
|
||||||
Operation string `yaml:"operation,omitempty"`
|
UserPassword string `yaml:"userPassword,omitempty"`
|
||||||
|
|
||||||
|
userMan usermanager.UserManager
|
||||||
|
|
||||||
|
// OS for the command, only used when type is user
|
||||||
|
OS string `yaml:"OS,omitempty"`
|
||||||
|
|
||||||
|
// UserOperation specifies the action for user-related commands (e.g., "create" or "remove")
|
||||||
|
UserOperation string `yaml:"userOperation,omitempty"`
|
||||||
|
|
||||||
|
userCmdSet bool
|
||||||
|
|
||||||
|
stdin *strings.Reader
|
||||||
}
|
}
|
||||||
|
|
||||||
RemoteSource struct {
|
RemoteSource struct {
|
||||||
@ -176,6 +192,9 @@ type (
|
|||||||
// Holds config file
|
// Holds config file
|
||||||
ConfigFilePath string
|
ConfigFilePath string
|
||||||
|
|
||||||
|
// Holds log file
|
||||||
|
LogFilePath string
|
||||||
|
|
||||||
// for command list file
|
// for command list file
|
||||||
CmdListFile string
|
CmdListFile string
|
||||||
|
|
||||||
@ -252,10 +271,9 @@ type (
|
|||||||
Final []string `yaml:"final,omitempty"`
|
Final []string `yaml:"final,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
CmdListResults struct {
|
CmdResult struct {
|
||||||
// name of the list
|
CmdName string // Name of the command executed
|
||||||
ListName string
|
ListName string // Name of the command list
|
||||||
// command that caused the list to fail
|
Error error // Error encountered, if any
|
||||||
ErrCmd string
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
package backy
|
package backy
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
@ -56,6 +57,13 @@ func SetCmdsToSearch(cmds []string) BackyOptionFunc {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetLogFile sets the path to the log file
|
||||||
|
func SetLogFile(logFile string) BackyOptionFunc {
|
||||||
|
return func(bco *ConfigOpts) {
|
||||||
|
bco.LogFilePath = logFile
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// cronEnabled enables the execution of command lists at specified times
|
// cronEnabled enables the execution of command lists at specified times
|
||||||
func CronEnabled() BackyOptionFunc {
|
func CronEnabled() BackyOptionFunc {
|
||||||
return func(bco *ConfigOpts) {
|
return func(bco *ConfigOpts) {
|
||||||
@ -234,9 +242,10 @@ func expandEnvVars(backyEnv map[string]string, envVars []string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// getPackageCommand checks for command type of package and if the command has already been set
|
// getCommandType checks for command type and if the command has already been set
|
||||||
// Returns the modified Command with the packageManager command as Cmd and the packageOperation as args, plus any additional Args
|
// Checks for types package and user
|
||||||
func getPackageCommand(command *Command) *Command {
|
// Returns the modified Command with the package- or userManager command as Cmd and the package- or userOperation as args, plus any additional Args
|
||||||
|
func getCommandType(command *Command) *Command {
|
||||||
|
|
||||||
if command.Type == "package" && !command.packageCmdSet {
|
if command.Type == "package" && !command.packageCmdSet {
|
||||||
command.packageCmdSet = true
|
command.packageCmdSet = true
|
||||||
@ -247,9 +256,71 @@ func getPackageCommand(command *Command) *Command {
|
|||||||
command.Cmd, command.Args = command.pkgMan.Remove(command.PackageName, command.Args)
|
command.Cmd, command.Args = command.pkgMan.Remove(command.PackageName, command.Args)
|
||||||
case "upgrade":
|
case "upgrade":
|
||||||
command.Cmd, command.Args = command.pkgMan.Upgrade(command.PackageName, command.PackageVersion)
|
command.Cmd, command.Args = command.pkgMan.Upgrade(command.PackageName, command.PackageVersion)
|
||||||
|
case "checkVersion":
|
||||||
|
command.Cmd, command.Args = command.pkgMan.CheckVersion(command.PackageName, command.PackageVersion)
|
||||||
}
|
}
|
||||||
} else if command.Type != "package" {
|
} else if command.Type != "package" {
|
||||||
command.packageCmdSet = false
|
command.packageCmdSet = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if command.Type == "user" && !command.userCmdSet {
|
||||||
|
command.userCmdSet = true
|
||||||
|
switch command.UserOperation {
|
||||||
|
case "add":
|
||||||
|
command.Cmd, command.Args = command.userMan.AddUser(
|
||||||
|
command.Username,
|
||||||
|
command.UserHome,
|
||||||
|
command.UserShell,
|
||||||
|
command.SystemUser,
|
||||||
|
command.UserGroups,
|
||||||
|
command.Args)
|
||||||
|
case "modify":
|
||||||
|
command.Cmd, command.Args = command.userMan.ModifyUser(
|
||||||
|
command.Username,
|
||||||
|
homeDir,
|
||||||
|
command.UserShell,
|
||||||
|
command.UserGroups)
|
||||||
|
case "checkIfExists":
|
||||||
|
command.Cmd, command.Args = command.userMan.UserExists(command.Username)
|
||||||
|
case "delete":
|
||||||
|
command.Cmd, command.Args = command.userMan.RemoveUser(command.Username)
|
||||||
|
case "password":
|
||||||
|
command.Cmd, command.stdin, command.UserPassword = command.userMan.ModifyPassword(command.Username, command.UserPassword)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return command
|
return command
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func parsePackageVersion(output string, cmdCtxLogger zerolog.Logger, command *Command, cmdOutBuf bytes.Buffer) ([]string, error) {
|
||||||
|
|
||||||
|
var err error
|
||||||
|
pkgVersion, err := command.pkgMan.Parse(output)
|
||||||
|
// println(output)
|
||||||
|
if err != nil {
|
||||||
|
cmdCtxLogger.Error().Err(err).Str("package", command.PackageName).Msg("Error parsing package version output")
|
||||||
|
return collectOutput(&cmdOutBuf, command.Name, cmdCtxLogger, command.GetOutput), err
|
||||||
|
}
|
||||||
|
|
||||||
|
cmdCtxLogger.Info().
|
||||||
|
Str("Installed", pkgVersion.Installed).
|
||||||
|
Str("Candidate", pkgVersion.Candidate).
|
||||||
|
Msg("Package version comparison")
|
||||||
|
|
||||||
|
if command.PackageVersion != "" {
|
||||||
|
if pkgVersion.Installed == command.PackageVersion {
|
||||||
|
cmdCtxLogger.Info().Msgf("Installed version matches specified version: %s", command.PackageVersion)
|
||||||
|
} else {
|
||||||
|
cmdCtxLogger.Info().Msgf("Installed version does not match specified version: %s", command.PackageVersion)
|
||||||
|
err = fmt.Errorf("Installed version does not match specified version: %s", command.PackageVersion)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if pkgVersion.Installed == pkgVersion.Candidate {
|
||||||
|
cmdCtxLogger.Info().Msg("Installed and Candidate versions match")
|
||||||
|
} else {
|
||||||
|
cmdCtxLogger.Info().Msg("Installed and Candidate versions differ")
|
||||||
|
err = errors.New("Installed and Candidate versions differ")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return collectOutput(&cmdOutBuf, command.Name, cmdCtxLogger, false), err
|
||||||
|
}
|
||||||
|
24
pkg/configfetcher/configfetcher.go
Normal file
24
pkg/configfetcher/configfetcher.go
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
package configfetcher
|
||||||
|
|
||||||
|
import "strings"
|
||||||
|
|
||||||
|
type ConfigFetcher interface {
|
||||||
|
// Fetch retrieves the configuration from the specified URL or source
|
||||||
|
// Returns the raw data as bytes or an error
|
||||||
|
Fetch(source string) ([]byte, error)
|
||||||
|
|
||||||
|
// Parse decodes the raw data into a Go structure (e.g., Commands, CommandLists)
|
||||||
|
// Takes the raw data as input and populates the target interface
|
||||||
|
Parse(data []byte, target interface{}) error
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewConfigFetcher(source string) ConfigFetcher {
|
||||||
|
if strings.HasPrefix(source, "http") || strings.HasPrefix(source, "https") {
|
||||||
|
return &HTTPFetcher{}
|
||||||
|
} else if strings.HasPrefix(source, "s3") {
|
||||||
|
return &S3Fetcher{}
|
||||||
|
} else {
|
||||||
|
return &LocalFetcher{}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
31
pkg/configfetcher/http.go
Normal file
31
pkg/configfetcher/http.go
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
package configfetcher
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"gopkg.in/yaml.v3"
|
||||||
|
)
|
||||||
|
|
||||||
|
type HTTPFetcher struct{}
|
||||||
|
|
||||||
|
// Fetch retrieves the configuration from the specified URL
|
||||||
|
func (h *HTTPFetcher) Fetch(source string) ([]byte, error) {
|
||||||
|
resp, err := http.Get(source)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
return nil, errors.New("failed to fetch remote config: " + resp.Status)
|
||||||
|
}
|
||||||
|
|
||||||
|
return io.ReadAll(resp.Body)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse decodes the raw data into the provided target structure
|
||||||
|
func (h *HTTPFetcher) Parse(data []byte, target interface{}) error {
|
||||||
|
return yaml.Unmarshal(data, target)
|
||||||
|
}
|
26
pkg/configfetcher/local.go
Normal file
26
pkg/configfetcher/local.go
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
package configfetcher
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"gopkg.in/yaml.v3"
|
||||||
|
)
|
||||||
|
|
||||||
|
type LocalFetcher struct{}
|
||||||
|
|
||||||
|
// Fetch retrieves the configuration from the specified local file path
|
||||||
|
func (l *LocalFetcher) Fetch(source string) ([]byte, error) {
|
||||||
|
file, err := os.Open(source)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
return io.ReadAll(file)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse decodes the raw data into the provided target structure
|
||||||
|
func (l *LocalFetcher) Parse(data []byte, target interface{}) error {
|
||||||
|
return yaml.Unmarshal(data, target)
|
||||||
|
}
|
66
pkg/configfetcher/s3.go
Normal file
66
pkg/configfetcher/s3.go
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
package configfetcher
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go-v2/config"
|
||||||
|
"github.com/aws/aws-sdk-go-v2/service/s3"
|
||||||
|
"gopkg.in/yaml.v3"
|
||||||
|
)
|
||||||
|
|
||||||
|
type S3Fetcher struct {
|
||||||
|
S3Client *s3.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewS3Fetcher creates a new instance of S3Fetcher with an initialized S3 client
|
||||||
|
func NewS3Fetcher() (*S3Fetcher, error) {
|
||||||
|
cfg, err := config.LoadDefaultConfig(context.TODO())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
client := s3.NewFromConfig(cfg)
|
||||||
|
return &S3Fetcher{S3Client: client}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch retrieves the configuration from an S3 bucket
|
||||||
|
// Source should be in the format "bucket-name/object-key"
|
||||||
|
func (s *S3Fetcher) Fetch(source string) ([]byte, error) {
|
||||||
|
bucket, key, err := parseS3Source(source)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := s.S3Client.GetObject(context.TODO(), &s3.GetObjectInput{
|
||||||
|
Bucket: &bucket,
|
||||||
|
Key: &key,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
_, err = buf.ReadFrom(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf.Bytes(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse decodes the raw data into the provided target structure
|
||||||
|
func (s *S3Fetcher) Parse(data []byte, target interface{}) error {
|
||||||
|
return yaml.Unmarshal(data, target)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper function to parse S3 source into bucket and key
|
||||||
|
func parseS3Source(source string) (bucket, key string, err error) {
|
||||||
|
parts := strings.SplitN(source, "/", 2)
|
||||||
|
if len(parts) != 2 {
|
||||||
|
return "", "", errors.New("invalid S3 source format, expected bucket-name/object-key")
|
||||||
|
}
|
||||||
|
return parts[0], parts[1], nil
|
||||||
|
}
|
@ -2,6 +2,8 @@ package apt
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"git.andrewnw.xyz/CyberShell/backy/pkg/pkgman/pkgcommon"
|
"git.andrewnw.xyz/CyberShell/backy/pkg/pkgman/pkgcommon"
|
||||||
)
|
)
|
||||||
@ -10,6 +12,7 @@ import (
|
|||||||
type AptManager struct {
|
type AptManager struct {
|
||||||
useAuth bool // Whether to use an authentication command
|
useAuth bool // Whether to use an authentication command
|
||||||
authCommand string // The authentication command, e.g., "sudo"
|
authCommand string // The authentication command, e.g., "sudo"
|
||||||
|
Parser pkgcommon.PackageParser
|
||||||
}
|
}
|
||||||
|
|
||||||
// DefaultAuthCommand is the default command used for authentication.
|
// DefaultAuthCommand is the default command used for authentication.
|
||||||
@ -62,6 +65,14 @@ func (a *AptManager) Upgrade(pkg, version string) (string, []string) {
|
|||||||
return baseCmd, baseArgs
|
return baseCmd, baseArgs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CheckVersion returns the command and arguments for checking the info of a specific package.
|
||||||
|
func (a *AptManager) CheckVersion(pkg, version string) (string, []string) {
|
||||||
|
baseCmd := a.prependAuthCommand("apt-cache")
|
||||||
|
baseArgs := []string{"policy", pkg}
|
||||||
|
|
||||||
|
return baseCmd, baseArgs
|
||||||
|
}
|
||||||
|
|
||||||
// UpgradeAll returns the command and arguments for upgrading all packages.
|
// UpgradeAll returns the command and arguments for upgrading all packages.
|
||||||
func (a *AptManager) UpgradeAll() (string, []string) {
|
func (a *AptManager) UpgradeAll() (string, []string) {
|
||||||
baseCmd := a.prependAuthCommand(DefaultPackageCommand)
|
baseCmd := a.prependAuthCommand(DefaultPackageCommand)
|
||||||
@ -93,3 +104,32 @@ func (a *AptManager) SetUseAuth(useAuth bool) {
|
|||||||
func (a *AptManager) SetAuthCommand(authCommand string) {
|
func (a *AptManager) SetAuthCommand(authCommand string) {
|
||||||
a.authCommand = authCommand
|
a.authCommand = authCommand
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetPackageParser assigns a PackageParser.
|
||||||
|
func (a *AptManager) SetPackageParser(parser pkgcommon.PackageParser) {
|
||||||
|
a.Parser = parser
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse parses the apt-cache policy output to extract Installed and Candidate versions.
|
||||||
|
func (a *AptManager) Parse(output string) (*pkgcommon.PackageVersion, error) {
|
||||||
|
// Check for error message in the output
|
||||||
|
if strings.Contains(output, "Unable to locate package") {
|
||||||
|
return nil, fmt.Errorf("error: %s", strings.TrimSpace(output))
|
||||||
|
}
|
||||||
|
|
||||||
|
reInstalled := regexp.MustCompile(`Installed:\s*([^\s]+)`)
|
||||||
|
reCandidate := regexp.MustCompile(`Candidate:\s*([^\s]+)`)
|
||||||
|
|
||||||
|
installedMatch := reInstalled.FindStringSubmatch(output)
|
||||||
|
candidateMatch := reCandidate.FindStringSubmatch(output)
|
||||||
|
|
||||||
|
if len(installedMatch) < 2 || len(candidateMatch) < 2 {
|
||||||
|
return nil, fmt.Errorf("failed to parse Installed or Candidate versions from apt output. check package name")
|
||||||
|
}
|
||||||
|
|
||||||
|
return &pkgcommon.PackageVersion{
|
||||||
|
Installed: strings.TrimSpace(installedMatch[1]),
|
||||||
|
Candidate: strings.TrimSpace(candidateMatch[1]),
|
||||||
|
Match: installedMatch[1] == candidateMatch[1],
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
@ -2,6 +2,8 @@ package dnf
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"git.andrewnw.xyz/CyberShell/backy/pkg/pkgman/pkgcommon"
|
"git.andrewnw.xyz/CyberShell/backy/pkg/pkgman/pkgcommon"
|
||||||
)
|
)
|
||||||
@ -74,6 +76,50 @@ func (y *DnfManager) UpgradeAll() (string, []string) {
|
|||||||
return baseCmd, baseArgs
|
return baseCmd, baseArgs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CheckVersion returns the command and arguments for checking the info of a specific package.
|
||||||
|
func (d *DnfManager) CheckVersion(pkg, version string) (string, []string) {
|
||||||
|
baseCmd := d.prependAuthCommand("dnf")
|
||||||
|
baseArgs := []string{"info", pkg}
|
||||||
|
|
||||||
|
return baseCmd, baseArgs
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse parses the dnf info output to extract Installed and Candidate versions.
|
||||||
|
func (d DnfManager) Parse(output string) (*pkgcommon.PackageVersion, error) {
|
||||||
|
|
||||||
|
// Check for error message in the output
|
||||||
|
if strings.Contains(output, "No matching packages to list") {
|
||||||
|
return nil, fmt.Errorf("error: package not listed")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Define regular expressions to capture installed and available versions
|
||||||
|
reInstalled := regexp.MustCompile(`(?m)^Installed packages\s*Name\s*:\s*\S+\s*Epoch\s*:\s*\S+\s*Version\s*:\s*([^\s]+)\s*Release\s*:\s*([^\s]+)`)
|
||||||
|
reAvailable := regexp.MustCompile(`(?m)^Available packages\s*Name\s*:\s*\S+\s*Epoch\s*:\s*\S+\s*Version\s*:\s*([^\s]+)\s*Release\s*:\s*([^\s]+)`)
|
||||||
|
|
||||||
|
installedMatch := reInstalled.FindStringSubmatch(output)
|
||||||
|
candidateMatch := reAvailable.FindStringSubmatch(output)
|
||||||
|
|
||||||
|
installedVersion := ""
|
||||||
|
candidateVersion := ""
|
||||||
|
|
||||||
|
if len(installedMatch) >= 3 {
|
||||||
|
installedVersion = fmt.Sprintf("%s-%s", installedMatch[1], installedMatch[2])
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(candidateMatch) >= 3 {
|
||||||
|
candidateVersion = fmt.Sprintf("%s-%s", candidateMatch[1], candidateMatch[2])
|
||||||
|
}
|
||||||
|
|
||||||
|
if installedVersion == "" && candidateVersion == "" {
|
||||||
|
return nil, fmt.Errorf("failed to parse versions from dnf output")
|
||||||
|
}
|
||||||
|
|
||||||
|
return &pkgcommon.PackageVersion{
|
||||||
|
Installed: installedVersion,
|
||||||
|
Candidate: candidateVersion,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
// prependAuthCommand prepends the authentication command if UseAuth is true.
|
// prependAuthCommand prepends the authentication command if UseAuth is true.
|
||||||
func (y *DnfManager) prependAuthCommand(baseCmd string) string {
|
func (y *DnfManager) prependAuthCommand(baseCmd string) string {
|
||||||
if y.useAuth {
|
if y.useAuth {
|
||||||
|
@ -2,3 +2,16 @@ package pkgcommon
|
|||||||
|
|
||||||
// PackageManagerOption defines a functional option for configuring a PackageManager.
|
// PackageManagerOption defines a functional option for configuring a PackageManager.
|
||||||
type PackageManagerOption func(interface{})
|
type PackageManagerOption func(interface{})
|
||||||
|
|
||||||
|
// PackageParser defines an interface for parsing package version information.
|
||||||
|
type PackageParser interface {
|
||||||
|
Parse(output string) (*PackageVersion, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PackageVersion represents the installed and candidate versions of a package.
|
||||||
|
type PackageVersion struct {
|
||||||
|
Installed string
|
||||||
|
Candidate string
|
||||||
|
Match bool
|
||||||
|
Message string
|
||||||
|
}
|
||||||
|
@ -15,7 +15,8 @@ type PackageManager interface {
|
|||||||
Remove(pkg string, args []string) (string, []string)
|
Remove(pkg string, args []string) (string, []string)
|
||||||
Upgrade(pkg, version string) (string, []string) // Upgrade a specific package
|
Upgrade(pkg, version string) (string, []string) // Upgrade a specific package
|
||||||
UpgradeAll() (string, []string)
|
UpgradeAll() (string, []string)
|
||||||
|
CheckVersion(pkg, version string) (string, []string)
|
||||||
|
Parse(output string) (*pkgcommon.PackageVersion, error)
|
||||||
// Configure applies functional options to customize the package manager.
|
// Configure applies functional options to customize the package manager.
|
||||||
Configure(options ...pkgcommon.PackageManagerOption)
|
Configure(options ...pkgcommon.PackageManagerOption)
|
||||||
}
|
}
|
||||||
@ -67,6 +68,8 @@ func WithoutAuth() pkgcommon.PackageManagerOption {
|
|||||||
|
|
||||||
// ConfigurablePackageManager defines methods for setting configuration options.
|
// ConfigurablePackageManager defines methods for setting configuration options.
|
||||||
type ConfigurablePackageManager interface {
|
type ConfigurablePackageManager interface {
|
||||||
|
pkgcommon.PackageParser
|
||||||
SetUseAuth(useAuth bool)
|
SetUseAuth(useAuth bool)
|
||||||
SetAuthCommand(authCommand string)
|
SetAuthCommand(authCommand string)
|
||||||
|
SetPackageParser(parser pkgcommon.PackageParser)
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ package yum
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"regexp"
|
||||||
|
|
||||||
"git.andrewnw.xyz/CyberShell/backy/pkg/pkgman/pkgcommon"
|
"git.andrewnw.xyz/CyberShell/backy/pkg/pkgman/pkgcommon"
|
||||||
)
|
)
|
||||||
@ -74,6 +75,43 @@ func (y *YumManager) UpgradeAll() (string, []string) {
|
|||||||
return baseCmd, baseArgs
|
return baseCmd, baseArgs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CheckVersion returns the command and arguments for checking the info of a specific package.
|
||||||
|
func (y *YumManager) CheckVersion(pkg, version string) (string, []string) {
|
||||||
|
baseCmd := y.prependAuthCommand("yum")
|
||||||
|
baseArgs := []string{"info", pkg}
|
||||||
|
|
||||||
|
return baseCmd, baseArgs
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse parses the dnf info output to extract Installed and Candidate versions.
|
||||||
|
func (y YumManager) Parse(output string) (*pkgcommon.PackageVersion, error) {
|
||||||
|
reInstalled := regexp.MustCompile(`(?m)^Installed Packages\s*Name\s*:\s*\S+\s*Version\s*:\s*([^\s]+)\s*Release\s*:\s*([^\s]+)`)
|
||||||
|
reAvailable := regexp.MustCompile(`(?m)^Available Packages\s*Name\s*:\s*\S+\s*Version\s*:\s*([^\s]+)\s*Release\s*:\s*([^\s]+)`)
|
||||||
|
|
||||||
|
installedMatch := reInstalled.FindStringSubmatch(output)
|
||||||
|
candidateMatch := reAvailable.FindStringSubmatch(output)
|
||||||
|
|
||||||
|
installedVersion := ""
|
||||||
|
candidateVersion := ""
|
||||||
|
|
||||||
|
if len(installedMatch) >= 3 {
|
||||||
|
installedVersion = fmt.Sprintf("%s-%s", installedMatch[1], installedMatch[2])
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(candidateMatch) >= 3 {
|
||||||
|
candidateVersion = fmt.Sprintf("%s-%s", candidateMatch[1], candidateMatch[2])
|
||||||
|
}
|
||||||
|
|
||||||
|
if installedVersion == "" && candidateVersion == "" {
|
||||||
|
return nil, fmt.Errorf("failed to parse versions from dnf output")
|
||||||
|
}
|
||||||
|
|
||||||
|
return &pkgcommon.PackageVersion{
|
||||||
|
Installed: installedVersion,
|
||||||
|
Candidate: candidateVersion,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
// prependAuthCommand prepends the authentication command if UseAuth is true.
|
// prependAuthCommand prepends the authentication command if UseAuth is true.
|
||||||
func (y *YumManager) prependAuthCommand(baseCmd string) string {
|
func (y *YumManager) prependAuthCommand(baseCmd string) string {
|
||||||
if y.useAuth {
|
if y.useAuth {
|
||||||
|
90
pkg/usermanager/linux/linux.go
Normal file
90
pkg/usermanager/linux/linux.go
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
package linux
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
passGen "github.com/sethvargo/go-password/password"
|
||||||
|
)
|
||||||
|
|
||||||
|
// LinuxUserManager implements UserManager for Linux systems.
|
||||||
|
type LinuxUserManager struct{}
|
||||||
|
|
||||||
|
func (l LinuxUserManager) NewLinuxManager() *LinuxUserManager {
|
||||||
|
return &LinuxUserManager{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddUser adds a new user to the system.
|
||||||
|
func (l LinuxUserManager) AddUser(username, homeDir, shell string, isSystem bool, groups, args []string) (string, []string) {
|
||||||
|
baseArgs := []string{}
|
||||||
|
|
||||||
|
if isSystem {
|
||||||
|
baseArgs = append(baseArgs, "--system")
|
||||||
|
}
|
||||||
|
|
||||||
|
if homeDir != "" {
|
||||||
|
baseArgs = append(baseArgs, "--home", homeDir)
|
||||||
|
}
|
||||||
|
|
||||||
|
if shell != "" {
|
||||||
|
baseArgs = append(baseArgs, "--shell", shell)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(groups) > 0 {
|
||||||
|
baseArgs = append(baseArgs, "--groups", strings.Join(groups, ","))
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(args) > 0 {
|
||||||
|
baseArgs = append(baseArgs, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
args = append(baseArgs, username)
|
||||||
|
|
||||||
|
cmd := "useradd"
|
||||||
|
return cmd, args
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l LinuxUserManager) ModifyPassword(username, password string) (string, *strings.Reader, string) {
|
||||||
|
cmd := "chpasswd"
|
||||||
|
if password == "" {
|
||||||
|
password = passGen.MustGenerate(20, 5, 5, false, false)
|
||||||
|
}
|
||||||
|
stdin := strings.NewReader(fmt.Sprintf("%s:%s", username, password))
|
||||||
|
return cmd, stdin, password
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveUser removes an existing user from the system.
|
||||||
|
func (l LinuxUserManager) RemoveUser(username string) (string, []string) {
|
||||||
|
cmd := "userdel"
|
||||||
|
|
||||||
|
return cmd, []string{username}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ModifyUser modifies an existing user's details.
|
||||||
|
func (l LinuxUserManager) ModifyUser(username, homeDir, shell string, groups []string) (string, []string) {
|
||||||
|
args := []string{}
|
||||||
|
|
||||||
|
if homeDir != "" {
|
||||||
|
args = append(args, "--home", homeDir)
|
||||||
|
}
|
||||||
|
|
||||||
|
if shell != "" {
|
||||||
|
args = append(args, "--shell", shell)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(groups) > 0 {
|
||||||
|
args = append(args, "--groups", strings.Join(groups, ","))
|
||||||
|
}
|
||||||
|
|
||||||
|
args = append(args, username)
|
||||||
|
|
||||||
|
cmd := "usermod"
|
||||||
|
|
||||||
|
return cmd, args
|
||||||
|
}
|
||||||
|
|
||||||
|
// UserExists checks if a user exists on the system.
|
||||||
|
func (l LinuxUserManager) UserExists(username string) (string, []string) {
|
||||||
|
cmd := "id"
|
||||||
|
return cmd, []string{username}
|
||||||
|
}
|
35
pkg/usermanager/userman.go
Normal file
35
pkg/usermanager/userman.go
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
package usermanager
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"git.andrewnw.xyz/CyberShell/backy/pkg/usermanager/linux"
|
||||||
|
)
|
||||||
|
|
||||||
|
// UserManager defines the interface for user management operations.
|
||||||
|
// All functions but one return a string for the command and any args.
|
||||||
|
type UserManager interface {
|
||||||
|
AddUser(username, homeDir, shell string, isSystem bool, groups, args []string) (string, []string)
|
||||||
|
RemoveUser(username string) (string, []string)
|
||||||
|
ModifyUser(username, homeDir, shell string, groups []string) (string, []string)
|
||||||
|
// Modify password uses chpasswd for Linux systems to build the command to change the password
|
||||||
|
// Should return a password as the last argument
|
||||||
|
// TODO: refactor when adding more systems instead of Linux
|
||||||
|
ModifyPassword(username, password string) (string, *strings.Reader, string)
|
||||||
|
UserExists(username string) (string, []string)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewUserManager(system string) (UserManager, error) {
|
||||||
|
var manager UserManager
|
||||||
|
|
||||||
|
switch system {
|
||||||
|
case "linux", "Linux":
|
||||||
|
manager = linux.LinuxUserManager{}
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("usermanger system %s is not recognized", system)
|
||||||
|
}
|
||||||
|
|
||||||
|
return manager, nil
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user