Compare commits
No commits in common. "bcba6b20864aa48ebf776734cfc8aa249faf3ad4" and "bb693dbb97c96f892bc659956f7a5a7342e1de56" have entirely different histories.
bcba6b2086
...
bb693dbb97
3
.changes/unreleased/Added-20250220-144549.yaml
Normal file
3
.changes/unreleased/Added-20250220-144549.yaml
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
kind: Added
|
||||||
|
body: '`list` command with subcommands `cmds` and `lists`'
|
||||||
|
time: 2025-02-20T14:45:49.562361581-06:00
|
3
.changes/unreleased/Added-20250220-145014.yaml
Normal file
3
.changes/unreleased/Added-20250220-145014.yaml
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
kind: Added
|
||||||
|
body: Deprecation and unsupported warnings for old config keys
|
||||||
|
time: 2025-02-20T14:50:14.048452348-06:00
|
3
.changes/unreleased/Added-20250220-145245.yaml
Normal file
3
.changes/unreleased/Added-20250220-145245.yaml
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
kind: Added
|
||||||
|
body: CLI flag `--cmdStdOut` to output command's stdout/stderr to stdout
|
||||||
|
time: 2025-02-20T14:52:45.030582408-06:00
|
3
.changes/unreleased/Added-20250221-003012.yaml
Normal file
3
.changes/unreleased/Added-20250221-003012.yaml
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
kind: Added
|
||||||
|
body: Command type `remoteScript`. See docs for more info.
|
||||||
|
time: 2025-02-21T00:30:12.276616792-06:00
|
@ -1,3 +0,0 @@
|
|||||||
kind: Added
|
|
||||||
body: 'Hooks: improved logging when executing'
|
|
||||||
time: 2025-03-01T13:29:32.195438013-06:00
|
|
@ -1,3 +0,0 @@
|
|||||||
kind: Added
|
|
||||||
body: 'User commands: adding SSH keys using config key `userSshPubKeys`'
|
|
||||||
time: 2025-03-03T23:42:48.009294808-06:00
|
|
@ -1,3 +0,0 @@
|
|||||||
kind: Added
|
|
||||||
body: 'directives: added support for fetching values using directive `%{externalSource:key}%`'
|
|
||||||
time: 2025-03-03T23:45:05.666939653-06:00
|
|
3
.changes/unreleased/Changed-20250223-153012.yaml
Normal file
3
.changes/unreleased/Changed-20250223-153012.yaml
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
kind: Changed
|
||||||
|
body: change to enums for Command type
|
||||||
|
time: 2025-02-23T15:30:12.033359922-06:00
|
3
.changes/unreleased/Changed-20250223-153305.yaml
Normal file
3
.changes/unreleased/Changed-20250223-153305.yaml
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
kind: Changed
|
||||||
|
body: Cache now stores resources by URL hash for ease-of-lookup
|
||||||
|
time: 2025-02-23T15:33:05.33444642-06:00
|
3
.changes/unreleased/Changed-20250228-173825.yaml
Normal file
3
.changes/unreleased/Changed-20250228-173825.yaml
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
kind: Changed
|
||||||
|
body: Changed PackageOperation to enums
|
||||||
|
time: 2025-02-28T17:38:25.426136588-06:00
|
@ -1,3 +0,0 @@
|
|||||||
kind: Changed
|
|
||||||
body: 'Commands: if dir is not specified, run in config dir'
|
|
||||||
time: 2025-03-01T19:43:21.323077376-06:00
|
|
3
.changes/unreleased/Fixed-20250220-144843.yaml
Normal file
3
.changes/unreleased/Fixed-20250220-144843.yaml
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
kind: Fixed
|
||||||
|
body: Local command's `dir` full path is now found with home directory
|
||||||
|
time: 2025-02-20T14:48:43.475300515-06:00
|
@ -1,3 +0,0 @@
|
|||||||
kind: Fixed
|
|
||||||
body: 'LocalFetcher: return fetch error'
|
|
||||||
time: 2025-03-01T13:26:00.330176712-06:00
|
|
@ -1,3 +0,0 @@
|
|||||||
kind: Fixed
|
|
||||||
body: 'Lists: load file key before attempting to load from current file'
|
|
||||||
time: 2025-03-01T13:28:01.739467944-06:00
|
|
@ -1,3 +0,0 @@
|
|||||||
kind: Fixed
|
|
||||||
body: 'fix: host not in config file, but in ssh config, properly added to hosts struct'
|
|
||||||
time: 2025-03-01T18:24:34.81395054-06:00
|
|
@ -1,12 +0,0 @@
|
|||||||
## v0.9.0 - 2025-02-28
|
|
||||||
### Added
|
|
||||||
* `list` command with subcommands `cmds` and `lists`
|
|
||||||
* Deprecation and unsupported warnings for old config keys
|
|
||||||
* CLI flag `--cmdStdOut` to output command's stdout/stderr to stdout
|
|
||||||
* Command type `remoteScript`. See docs for more info.
|
|
||||||
### Changed
|
|
||||||
* change to enums for Command type
|
|
||||||
* Cache now stores resources by URL hash for ease-of-lookup
|
|
||||||
* Changed PackageOperation to enums
|
|
||||||
### Fixed
|
|
||||||
* Local command's `dir` full path is now found with home directory
|
|
@ -1,3 +0,0 @@
|
|||||||
## v0.9.1 - 2025-03-01
|
|
||||||
### Changed
|
|
||||||
* Use EnvVar AWS_PROFILE to get S3 profile
|
|
17
CHANGELOG.md
17
CHANGELOG.md
@ -6,23 +6,6 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html),
|
|||||||
and is generated by [Changie](https://github.com/miniscruff/changie).
|
and is generated by [Changie](https://github.com/miniscruff/changie).
|
||||||
|
|
||||||
|
|
||||||
## v0.9.1 - 2025-03-01
|
|
||||||
### Changed
|
|
||||||
* Use EnvVar AWS_PROFILE to get S3 profile
|
|
||||||
|
|
||||||
## v0.9.0 - 2025-02-28
|
|
||||||
### Added
|
|
||||||
* `list` command with subcommands `cmds` and `lists`
|
|
||||||
* Deprecation and unsupported warnings for old config keys
|
|
||||||
* CLI flag `--cmdStdOut` to output command's stdout/stderr to stdout
|
|
||||||
* Command type `remoteScript`. See docs for more info.
|
|
||||||
### Changed
|
|
||||||
* change to enums for Command type
|
|
||||||
* Cache now stores resources by URL hash for ease-of-lookup
|
|
||||||
* Changed PackageOperation to enums
|
|
||||||
### Fixed
|
|
||||||
* Local command's `dir` full path is now found with home directory
|
|
||||||
|
|
||||||
## v0.8.0 - 2025-02-15
|
## v0.8.0 - 2025-02-15
|
||||||
### Changed
|
### Changed
|
||||||
* Breaking: `cmd-lists` key changed to `cmdLists`
|
* Breaking: `cmd-lists` key changed to `cmdLists`
|
||||||
|
@ -8,17 +8,14 @@
|
|||||||
"settings": {
|
"settings": {
|
||||||
"cSpell.words": [
|
"cSpell.words": [
|
||||||
"Autorestic",
|
"Autorestic",
|
||||||
"changie",
|
|
||||||
"Cmds",
|
"Cmds",
|
||||||
"CMDSTDOUT",
|
"CMDSTDOUT",
|
||||||
"goreleaser",
|
|
||||||
"knadh",
|
"knadh",
|
||||||
"koanf",
|
"koanf",
|
||||||
"mattn",
|
"mattn",
|
||||||
"maunium",
|
"maunium",
|
||||||
"mautrix",
|
"mautrix",
|
||||||
"nikoksr",
|
"nikoksr",
|
||||||
"rawbytes",
|
|
||||||
"remotefetcher",
|
"remotefetcher",
|
||||||
"Strs"
|
"Strs"
|
||||||
]
|
]
|
||||||
|
@ -7,7 +7,7 @@ import (
|
|||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
const versionStr = "0.9.2"
|
const versionStr = "0.9.0"
|
||||||
|
|
||||||
var (
|
var (
|
||||||
versionCmd = &cobra.Command{
|
versionCmd = &cobra.Command{
|
||||||
|
@ -8,7 +8,32 @@ weight: 1
|
|||||||
|
|
||||||
### Example Config
|
### Example Config
|
||||||
|
|
||||||
{{% code file="/examples/example.yml" language="yaml" %}}
|
```yaml
|
||||||
|
commands:
|
||||||
|
stop-docker-container:
|
||||||
|
cmd: docker
|
||||||
|
Args:
|
||||||
|
- compose
|
||||||
|
- -f /some/path/to/docker-compose.yaml
|
||||||
|
- down
|
||||||
|
# if host is not defined, command will be run locally
|
||||||
|
# The host has to be defined in either the config file or the SSH Config files
|
||||||
|
host: some-host
|
||||||
|
hooks
|
||||||
|
error:
|
||||||
|
- some-other-command-when-failing
|
||||||
|
success:
|
||||||
|
- success-command
|
||||||
|
final:
|
||||||
|
- final-command
|
||||||
|
backup-docker-container-script:
|
||||||
|
cmd: /path/to/local/script
|
||||||
|
# script file is input as stdin to SSH
|
||||||
|
type: scriptFile # also can be script
|
||||||
|
environment:
|
||||||
|
- FOO=BAR
|
||||||
|
- APP=$VAR
|
||||||
|
```
|
||||||
|
|
||||||
Values available for this section **(case-sensitive)**:
|
Values available for this section **(case-sensitive)**:
|
||||||
|
|
||||||
|
@ -12,10 +12,6 @@ For the main config file to be fetched remotely, pass the URL using `-f [url]`.
|
|||||||
|
|
||||||
If using S3, you should use the s3 protocol URI: `s3://bucketName/key/path`. You will also need to set the env variable `S3_ENDPOINT` to the appropriate value. The flag `--s3-endpoint` can be used to override this value or to set this value, if not already set.
|
If using S3, you should use the s3 protocol URI: `s3://bucketName/key/path`. You will also need to set the env variable `S3_ENDPOINT` to the appropriate value. The flag `--s3-endpoint` can be used to override this value or to set this value, if not already set.
|
||||||
|
|
||||||
## Authentication
|
|
||||||
|
|
||||||
Currently, only the AWS authentication credentials file `~/.aws/credentials` is supported. For now, the environment variable `AWS_PROFILE` is used to lookup the profile.
|
|
||||||
|
|
||||||
## Scripts
|
## Scripts
|
||||||
|
|
||||||
Remote script support is currently limited to http/https endpoints.
|
Remote script support is currently limited to http/https endpoints.
|
@ -1,108 +0,0 @@
|
|||||||
commands:
|
|
||||||
stop-docker-container:
|
|
||||||
cmd: docker
|
|
||||||
Args:
|
|
||||||
- compose
|
|
||||||
- -f /some/path/to/docker-compose.yaml
|
|
||||||
- down
|
|
||||||
# if host is not defined, cmd will be run locally
|
|
||||||
host: some-host
|
|
||||||
hooks:
|
|
||||||
final:
|
|
||||||
- hostname
|
|
||||||
error:
|
|
||||||
- hostname
|
|
||||||
backup-docker-container-script:
|
|
||||||
cmd: /path/to/script
|
|
||||||
# The host has to be defined in the config file
|
|
||||||
host: some-host
|
|
||||||
environment:
|
|
||||||
- FOO=BAR
|
|
||||||
- APP=$VAR
|
|
||||||
shell-cmd:
|
|
||||||
cmd: rsync
|
|
||||||
shell: bash
|
|
||||||
Args:
|
|
||||||
- -av some-host:/path/to/data ~/Docker/Backups/docker-data
|
|
||||||
hostname:
|
|
||||||
cmd: hostname
|
|
||||||
update-docker:
|
|
||||||
type: package
|
|
||||||
shell: zsh # best to run package commands in a shell
|
|
||||||
packageName: docker-ce
|
|
||||||
Args:
|
|
||||||
- docker-ce-cli
|
|
||||||
packageManager: apt
|
|
||||||
packageOperation: install
|
|
||||||
update-dockerApt:
|
|
||||||
# type: package
|
|
||||||
shell: zsh
|
|
||||||
cmd: apt
|
|
||||||
Args:
|
|
||||||
- update
|
|
||||||
- "&&"
|
|
||||||
- apt install -y docker-ce
|
|
||||||
- docker-ce-cli
|
|
||||||
packageManager: apt
|
|
||||||
packageOperation: install
|
|
||||||
|
|
||||||
cmd-lists:
|
|
||||||
cmds-to-run: # this can be any name you want
|
|
||||||
# all commands have to be defined
|
|
||||||
order:
|
|
||||||
- stop-docker-container
|
|
||||||
- backup-docker-container-script
|
|
||||||
- shell-cmd
|
|
||||||
- hostname
|
|
||||||
notifications:
|
|
||||||
- matrix.matrix
|
|
||||||
name: backup-some-server
|
|
||||||
cron: "0 0 1 * * *"
|
|
||||||
hostname:
|
|
||||||
name: hostname
|
|
||||||
order:
|
|
||||||
- hostname
|
|
||||||
notifications:
|
|
||||||
- mail.prod-email
|
|
||||||
|
|
||||||
hosts:
|
|
||||||
# any ssh_config(5) keys/values not listed here will be looked up in the config file or the default config file
|
|
||||||
some-host:
|
|
||||||
hostname: some-hostname
|
|
||||||
config: ~/.ssh/config
|
|
||||||
user: user
|
|
||||||
privatekeypath: /path/to/private/key
|
|
||||||
port: 22
|
|
||||||
# can also be env:VAR
|
|
||||||
password: file:/path/to/file
|
|
||||||
# only one is supported for now
|
|
||||||
proxyjump: some-proxy-host
|
|
||||||
|
|
||||||
# optional
|
|
||||||
logging:
|
|
||||||
verbose: true
|
|
||||||
file: ./backy.log
|
|
||||||
console: false
|
|
||||||
cmd-std-out: false
|
|
||||||
|
|
||||||
|
|
||||||
notifications:
|
|
||||||
mail:
|
|
||||||
prod-email:
|
|
||||||
id: prod-email
|
|
||||||
type: mail
|
|
||||||
host: yourhost.tld
|
|
||||||
port: 587
|
|
||||||
senderAddress: email@domain.tld
|
|
||||||
to:
|
|
||||||
- admin@domain.tld
|
|
||||||
username: smtp-username@domain.tld
|
|
||||||
password: your-password-here
|
|
||||||
matrix:
|
|
||||||
matrix:
|
|
||||||
id: matrix
|
|
||||||
type: matrix
|
|
||||||
home-server: your-home-server.tld
|
|
||||||
room-id: room-id
|
|
||||||
access-token: your-access-token
|
|
||||||
user-id: your-user-id
|
|
@ -1,24 +0,0 @@
|
|||||||
commands:
|
|
||||||
stop-docker-container:
|
|
||||||
cmd: docker
|
|
||||||
Args:
|
|
||||||
- compose
|
|
||||||
- -f /some/path/to/docker-compose.yaml
|
|
||||||
- down
|
|
||||||
# if host is not defined, command will be run locally
|
|
||||||
# The host has to be defined in either the config file or the SSH Config files
|
|
||||||
host: some-host
|
|
||||||
hooks:
|
|
||||||
error:
|
|
||||||
- some-other-command-when-failing
|
|
||||||
success:
|
|
||||||
- success-command
|
|
||||||
final:
|
|
||||||
- final-command
|
|
||||||
backup-docker-container-script:
|
|
||||||
cmd: /path/to/local/script
|
|
||||||
# script file is input as stdin to SSH
|
|
||||||
type: scriptFile # also can be script
|
|
||||||
environment:
|
|
||||||
- FOO=BAR
|
|
||||||
- APP=$VAR
|
|
@ -1,3 +0,0 @@
|
|||||||
{{ $file := .Get "file" | readFile }}
|
|
||||||
{{ $lang := .Get "language" }}
|
|
||||||
{{ (print "```" $lang "\n" $file "\n```") }}
|
|
@ -1,24 +0,0 @@
|
|||||||
commands:
|
|
||||||
stop-docker-container:
|
|
||||||
cmd: docker
|
|
||||||
Args:
|
|
||||||
- compose
|
|
||||||
- -f /some/path/to/docker-compose.yaml
|
|
||||||
- down
|
|
||||||
# if host is not defined, command will be run locally
|
|
||||||
# The host has to be defined in either the config file or the SSH Config files
|
|
||||||
host: some-host
|
|
||||||
hooks:
|
|
||||||
error:
|
|
||||||
- some-other-command-when-failing
|
|
||||||
success:
|
|
||||||
- success-command
|
|
||||||
final:
|
|
||||||
- final-command
|
|
||||||
backup-docker-container-script:
|
|
||||||
cmd: /path/to/local/script
|
|
||||||
# script file is input as stdin to SSH
|
|
||||||
type: scriptFile # also can be script
|
|
||||||
environment:
|
|
||||||
- FOO=BAR
|
|
||||||
- APP=$VAR
|
|
2
go.mod
2
go.mod
@ -21,7 +21,6 @@ require (
|
|||||||
github.com/mitchellh/go-homedir v1.1.0
|
github.com/mitchellh/go-homedir v1.1.0
|
||||||
github.com/nikoksr/notify v1.3.0
|
github.com/nikoksr/notify v1.3.0
|
||||||
github.com/pkg/errors v0.9.1
|
github.com/pkg/errors v0.9.1
|
||||||
github.com/pkg/sftp v1.13.7
|
|
||||||
github.com/rs/zerolog v1.33.0
|
github.com/rs/zerolog v1.33.0
|
||||||
github.com/sethvargo/go-password v0.3.1
|
github.com/sethvargo/go-password v0.3.1
|
||||||
github.com/spf13/cobra v1.8.1
|
github.com/spf13/cobra v1.8.1
|
||||||
@ -66,7 +65,6 @@ require (
|
|||||||
github.com/klauspost/compress v1.17.11 // indirect
|
github.com/klauspost/compress v1.17.11 // indirect
|
||||||
github.com/klauspost/cpuid/v2 v2.2.9 // indirect
|
github.com/klauspost/cpuid/v2 v2.2.9 // indirect
|
||||||
github.com/knadh/koanf/maps v0.1.1 // indirect
|
github.com/knadh/koanf/maps v0.1.1 // indirect
|
||||||
github.com/kr/fs v0.1.0 // indirect
|
|
||||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||||
github.com/minio/md5-simd v1.1.2 // indirect
|
github.com/minio/md5-simd v1.1.2 // indirect
|
||||||
github.com/mitchellh/copystructure v1.2.0 // indirect
|
github.com/mitchellh/copystructure v1.2.0 // indirect
|
||||||
|
42
go.sum
42
go.sum
@ -100,8 +100,6 @@ github.com/knadh/koanf/providers/rawbytes v0.1.0 h1:dpzgu2KO6uf6oCb4aP05KDmKmAmI
|
|||||||
github.com/knadh/koanf/providers/rawbytes v0.1.0/go.mod h1:mMTB1/IcJ/yE++A2iEZbY1MLygX7vttU+C+S/YmPu9c=
|
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.1.2 h1:I2rtLRqXRy1p01m/utEtpZSSA6dcJbgGVuE27kW2PzQ=
|
github.com/knadh/koanf/v2 v2.1.2 h1:I2rtLRqXRy1p01m/utEtpZSSA6dcJbgGVuE27kW2PzQ=
|
||||||
github.com/knadh/koanf/v2 v2.1.2/go.mod h1:Gphfaen0q1Fc1HTgJgSTC4oRX9R2R5ErYMZJy8fLJBo=
|
github.com/knadh/koanf/v2 v2.1.2/go.mod h1:Gphfaen0q1Fc1HTgJgSTC4oRX9R2R5ErYMZJy8fLJBo=
|
||||||
github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8=
|
|
||||||
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
|
|
||||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||||
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
|
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
|
||||||
@ -137,8 +135,6 @@ github.com/pascaldekloe/name v1.0.0/go.mod h1:Z//MfYJnH4jVpQ9wkclwu2I2MkHmXTlT9w
|
|||||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pkg/sftp v1.13.7 h1:uv+I3nNJvlKZIQGSr8JVQLNHFU9YhhNpvC14Y6KgmSM=
|
|
||||||
github.com/pkg/sftp v1.13.7/go.mod h1:KMKI0t3T6hfA+lTR/ssZdunHo+uwq7ghoN09/FSu3DY=
|
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
|
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
|
||||||
@ -183,72 +179,34 @@ github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4=
|
|||||||
github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
||||||
github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY=
|
github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY=
|
||||||
github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28=
|
github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28=
|
||||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
|
||||||
go.mau.fi/util v0.8.4 h1:mVKlJcXWfVo8ZW3f4vqtjGpqtZqJvX4ETekxawt2vnQ=
|
go.mau.fi/util v0.8.4 h1:mVKlJcXWfVo8ZW3f4vqtjGpqtZqJvX4ETekxawt2vnQ=
|
||||||
go.mau.fi/util v0.8.4/go.mod h1:MOfGTs1CBuK6ERTcSL4lb5YU7/ujz09eOPVEDckuazY=
|
go.mau.fi/util v0.8.4/go.mod h1:MOfGTs1CBuK6ERTcSL4lb5YU7/ujz09eOPVEDckuazY=
|
||||||
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||||
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
|
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
|
||||||
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
|
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
|
||||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
|
||||||
golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
|
|
||||||
golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus=
|
golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus=
|
||||||
golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=
|
golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=
|
||||||
golang.org/x/exp v0.0.0-20250207012021-f9890c6ad9f3 h1:qNgPs5exUA+G0C96DrPwNrvLSj7GT/9D+3WMWUcUg34=
|
golang.org/x/exp v0.0.0-20250207012021-f9890c6ad9f3 h1:qNgPs5exUA+G0C96DrPwNrvLSj7GT/9D+3WMWUcUg34=
|
||||||
golang.org/x/exp v0.0.0-20250207012021-f9890c6ad9f3/go.mod h1:tujkw807nyEEAamNbDrEGzRav+ilXA7PCRAd6xsmwiU=
|
golang.org/x/exp v0.0.0-20250207012021-f9890c6ad9f3/go.mod h1:tujkw807nyEEAamNbDrEGzRav+ilXA7PCRAd6xsmwiU=
|
||||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
|
||||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
|
||||||
golang.org/x/mod v0.23.0 h1:Zb7khfcRGKk+kqfxFaP5tZqCnDZMjC5VtUBs87Hr6QM=
|
golang.org/x/mod v0.23.0 h1:Zb7khfcRGKk+kqfxFaP5tZqCnDZMjC5VtUBs87Hr6QM=
|
||||||
golang.org/x/mod v0.23.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
|
golang.org/x/mod v0.23.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
|
||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
|
||||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
|
||||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
|
||||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
|
||||||
golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8=
|
golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8=
|
||||||
golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk=
|
golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w=
|
golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w=
|
||||||
golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
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/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
|
||||||
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
|
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
|
||||||
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
|
||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
|
||||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
|
||||||
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
|
||||||
golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0=
|
|
||||||
golang.org/x/term v0.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU=
|
golang.org/x/term v0.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU=
|
||||||
golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s=
|
golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
|
||||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
|
||||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
|
||||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
|
||||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
|
||||||
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
|
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
|
||||||
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
|
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
|
||||||
golang.org/x/time v0.10.0 h1:3usCWA8tQn0L8+hFJQNgzpWbd89begxN66o1Ojdn5L4=
|
golang.org/x/time v0.10.0 h1:3usCWA8tQn0L8+hFJQNgzpWbd89begxN66o1Ojdn5L4=
|
||||||
golang.org/x/time v0.10.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
golang.org/x/time v0.10.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
|
||||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
|
||||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
|
||||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
|
||||||
golang.org/x/tools v0.30.0 h1:BgcpHewrV5AUp2G9MebG4XPFI1E2W41zU1SaqVA9vJY=
|
golang.org/x/tools v0.30.0 h1:BgcpHewrV5AUp2G9MebG4XPFI1E2W41zU1SaqVA9vJY=
|
||||||
golang.org/x/tools v0.30.0/go.mod h1:c347cR/OJfw5TI+GfX7RUPNMdDRRbjvYTS0jPyvsVtY=
|
golang.org/x/tools v0.30.0/go.mod h1:c347cR/OJfw5TI+GfX7RUPNMdDRRbjvYTS0jPyvsVtY=
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||||
|
@ -144,7 +144,6 @@ func (command *Command) RunCmd(cmdCtxLogger zerolog.Logger, opts *ConfigOpts) ([
|
|||||||
}
|
}
|
||||||
|
|
||||||
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()
|
||||||
|
|
||||||
@ -183,7 +182,20 @@ func (command *Command) RunCmd(cmdCtxLogger zerolog.Logger, opts *ConfigOpts) ([
|
|||||||
|
|
||||||
err = localCMD.Run()
|
err = localCMD.Run()
|
||||||
|
|
||||||
outputArr = logCommandOutput(command, cmdOutBuf, cmdCtxLogger, outputArr)
|
outScanner := bufio.NewScanner(&cmdOutBuf)
|
||||||
|
|
||||||
|
for outScanner.Scan() {
|
||||||
|
outMap := make(map[string]interface{})
|
||||||
|
outMap["cmd"] = command.Cmd
|
||||||
|
outMap["output"] = outScanner.Text()
|
||||||
|
|
||||||
|
if str, ok := outMap["output"].(string); ok {
|
||||||
|
outputArr = append(outputArr, str)
|
||||||
|
}
|
||||||
|
// if command.GetOutput {
|
||||||
|
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()
|
||||||
return outputArr, err
|
return outputArr, err
|
||||||
@ -228,7 +240,7 @@ func cmdListWorker(msgTemps *msgTemplates, jobs <-chan *CmdList, results chan<-
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Collect output if required
|
// Collect output if required
|
||||||
if list.GetOutput || cmdToRun.GetOutputInList {
|
if list.GetOutput || cmdToRun.GetOutput {
|
||||||
outStructArr = append(outStructArr, outStruct{
|
outStructArr = append(outStructArr, outStruct{
|
||||||
CmdName: currentCmd,
|
CmdName: currentCmd,
|
||||||
CmdExecuted: currentCmd,
|
CmdExecuted: currentCmd,
|
||||||
@ -237,14 +249,17 @@ func cmdListWorker(msgTemps *msgTemplates, jobs <-chan *CmdList, results chan<-
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Notify success if no errors occurred
|
||||||
if !hasError && list.NotifyConfig != nil && (list.NotifyOnSuccess || list.GetOutput) {
|
if !hasError && list.NotifyConfig != nil && (list.NotifyOnSuccess || list.GetOutput) {
|
||||||
notifySuccess(cmdLogger, msgTemps, list, cmdsRan, outStructArr)
|
notifySuccess(cmdLogger, msgTemps, list, cmdsRan, outStructArr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Execute success and final hooks for all commands
|
||||||
for _, cmd := range list.Order {
|
for _, cmd := range list.Order {
|
||||||
cmdToRun := opts.Cmds[cmd]
|
cmdToRun := opts.Cmds[cmd]
|
||||||
|
|
||||||
if !hasError {
|
// Execute success hooks if the command succeeded
|
||||||
|
if !hasError || cmdsRanContains(cmd, cmdsRan) {
|
||||||
cmdToRun.ExecuteHooks("success", opts)
|
cmdToRun.ExecuteHooks("success", opts)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -261,6 +276,17 @@ func cmdListWorker(msgTemps *msgTemplates, jobs <-chan *CmdList, results chan<-
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Helper to check if a command is in the list of executed commands
|
||||||
|
func cmdsRanContains(cmd string, cmdsRan []string) bool {
|
||||||
|
for _, c := range cmdsRan {
|
||||||
|
if c == cmd {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper to notify errors
|
||||||
func notifyError(logger zerolog.Logger, templates *msgTemplates, list *CmdList, cmdsRan []string, outStructArr []outStruct, err error, cmd *Command) {
|
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,
|
||||||
@ -329,6 +355,7 @@ func (opts *ConfigOpts) RunListConfig(cron string) {
|
|||||||
result := <-results
|
result := <-results
|
||||||
opts.Logger.Debug().Msgf("Processing result for list %s, command %s", result.ListName, result.CmdName)
|
opts.Logger.Debug().Msgf("Processing result for list %s, command %s", result.ListName, result.CmdName)
|
||||||
|
|
||||||
|
// Process final hooks for the list (already handled in worker)
|
||||||
}
|
}
|
||||||
opts.closeHostConnections()
|
opts.closeHostConnections()
|
||||||
}
|
}
|
||||||
@ -340,8 +367,10 @@ func (opts *ConfigOpts) ExecuteCmds() {
|
|||||||
_, runErr := cmdToRun.RunCmd(cmdLogger, opts)
|
_, runErr := cmdToRun.RunCmd(cmdLogger, opts)
|
||||||
if runErr != nil {
|
if runErr != nil {
|
||||||
opts.Logger.Err(runErr).Send()
|
opts.Logger.Err(runErr).Send()
|
||||||
|
|
||||||
cmdToRun.ExecuteHooks("error", opts)
|
cmdToRun.ExecuteHooks("error", opts)
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
cmdToRun.ExecuteHooks("success", opts)
|
cmdToRun.ExecuteHooks("success", opts)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -349,6 +378,7 @@ func (opts *ConfigOpts) ExecuteCmds() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
opts.closeHostConnections()
|
opts.closeHostConnections()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ConfigOpts) closeHostConnections() {
|
func (c *ConfigOpts) closeHostConnections() {
|
||||||
@ -395,9 +425,8 @@ func (cmd *Command) ExecuteHooks(hookType string, opts *ConfigOpts) {
|
|||||||
case "error":
|
case "error":
|
||||||
for _, v := range cmd.Hooks.Error {
|
for _, v := range cmd.Hooks.Error {
|
||||||
errCmd := opts.Cmds[v]
|
errCmd := opts.Cmds[v]
|
||||||
opts.Logger.Info().Msgf("Running error hook command %s", v)
|
|
||||||
cmdLogger := opts.Logger.With().
|
cmdLogger := opts.Logger.With().
|
||||||
Str("backy-cmd", v).Str("hookType", "error").
|
Str("backy-cmd", v).
|
||||||
Logger()
|
Logger()
|
||||||
errCmd.RunCmd(cmdLogger, opts)
|
errCmd.RunCmd(cmdLogger, opts)
|
||||||
}
|
}
|
||||||
@ -405,18 +434,16 @@ func (cmd *Command) ExecuteHooks(hookType string, opts *ConfigOpts) {
|
|||||||
case "success":
|
case "success":
|
||||||
for _, v := range cmd.Hooks.Success {
|
for _, v := range cmd.Hooks.Success {
|
||||||
successCmd := opts.Cmds[v]
|
successCmd := opts.Cmds[v]
|
||||||
opts.Logger.Info().Msgf("Running success hook command %s", v)
|
|
||||||
cmdLogger := opts.Logger.With().
|
cmdLogger := opts.Logger.With().
|
||||||
Str("backy-cmd", v).Str("hookType", "success").
|
Str("backy-cmd", v).
|
||||||
Logger()
|
Logger()
|
||||||
successCmd.RunCmd(cmdLogger, opts)
|
successCmd.RunCmd(cmdLogger, opts)
|
||||||
}
|
}
|
||||||
case "final":
|
case "final":
|
||||||
for _, v := range cmd.Hooks.Final {
|
for _, v := range cmd.Hooks.Final {
|
||||||
finalCmd := opts.Cmds[v]
|
finalCmd := opts.Cmds[v]
|
||||||
opts.Logger.Info().Msgf("Running final hook command %s", v)
|
|
||||||
cmdLogger := opts.Logger.With().
|
cmdLogger := opts.Logger.With().
|
||||||
Str("backy-cmd", v).Str("hookType", "final").
|
Str("backy-cmd", v).
|
||||||
Logger()
|
Logger()
|
||||||
finalCmd.RunCmd(cmdLogger, opts)
|
finalCmd.RunCmd(cmdLogger, opts)
|
||||||
}
|
}
|
||||||
@ -454,25 +481,6 @@ func (opts *ConfigOpts) ExecCmdsSSH(cmdList []string, hostsList []string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func logCommandOutput(command *Command, cmdOutBuf bytes.Buffer, cmdCtxLogger zerolog.Logger, outputArr []string) []string {
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
if command.OutputToLog {
|
|
||||||
cmdCtxLogger.Info().Fields(outMap).Send()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return outputArr
|
|
||||||
}
|
|
||||||
|
|
||||||
// func executeUserCommands() []string {
|
// func executeUserCommands() []string {
|
||||||
|
|
||||||
// }
|
// }
|
||||||
|
@ -22,13 +22,10 @@ import (
|
|||||||
"github.com/rs/zerolog"
|
"github.com/rs/zerolog"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const macroStart string = "%{"
|
||||||
externDirectiveStart string = "%{"
|
const macroEnd string = "}%"
|
||||||
externDirectiveEnd string = "}%"
|
const envMacroStart string = "%{env:"
|
||||||
externFileDirectiveStart string = "%{file:"
|
const vaultMacroStart string = "%{vault:"
|
||||||
envExternDirectiveStart string = "%{env:"
|
|
||||||
vaultExternDirectiveStart string = "%{vault:"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (opts *ConfigOpts) InitConfig() {
|
func (opts *ConfigOpts) InitConfig() {
|
||||||
var err error
|
var err error
|
||||||
@ -98,6 +95,41 @@ func (opts *ConfigOpts) InitConfig() {
|
|||||||
opts.koanf = backyKoanf
|
opts.koanf = backyKoanf
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func loadConfigFile(fetcher remotefetcher.RemoteFetcher, filePath string, k *koanf.Koanf, opts *ConfigOpts) {
|
||||||
|
data, err := fetcher.Fetch(filePath)
|
||||||
|
if err != nil {
|
||||||
|
logging.ExitWithMSG(fmt.Sprintf("Could not fetch config file %s: %v", filePath, err), 1, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
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 remotefetcher.RemoteFetcher, configFiles []string, k *koanf.Koanf, opts *ConfigOpts) {
|
||||||
|
cFileFailures := 0
|
||||||
|
for _, c := range configFiles {
|
||||||
|
opts.ConfigFilePath = c
|
||||||
|
data, err := fetcher.Fetch(c)
|
||||||
|
if err != nil {
|
||||||
|
cFileFailures++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if data != nil {
|
||||||
|
if err := k.Load(rawbytes.Provider(data), yaml.Parser()); err == nil {
|
||||||
|
break
|
||||||
|
} else {
|
||||||
|
logging.ExitWithMSG(fmt.Sprintf("error loading config from file %s: %v", c, err), 1, &opts.Logger)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if cFileFailures == len(configFiles) {
|
||||||
|
logging.ExitWithMSG("Could not find any valid local config file", 1, nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (opts *ConfigOpts) ReadConfig() *ConfigOpts {
|
func (opts *ConfigOpts) ReadConfig() *ConfigOpts {
|
||||||
setTerminalEnv()
|
setTerminalEnv()
|
||||||
|
|
||||||
@ -116,7 +148,7 @@ func (opts *ConfigOpts) ReadConfig() *ConfigOpts {
|
|||||||
|
|
||||||
CheckConfigValues(backyKoanf, opts.ConfigFilePath)
|
CheckConfigValues(backyKoanf, opts.ConfigFilePath)
|
||||||
|
|
||||||
validateExecCommandsFromCLI(backyKoanf, opts)
|
validateCommands(backyKoanf, opts)
|
||||||
|
|
||||||
setLoggingOptions(backyKoanf, opts)
|
setLoggingOptions(backyKoanf, opts)
|
||||||
|
|
||||||
@ -160,41 +192,6 @@ func (opts *ConfigOpts) ReadConfig() *ConfigOpts {
|
|||||||
return opts
|
return opts
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadConfigFile(fetcher remotefetcher.RemoteFetcher, filePath string, k *koanf.Koanf, opts *ConfigOpts) {
|
|
||||||
data, err := fetcher.Fetch(filePath)
|
|
||||||
if err != nil {
|
|
||||||
logging.ExitWithMSG(generateFileFetchErrorString(filePath, "config", err), 1, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
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 remotefetcher.RemoteFetcher, configFiles []string, k *koanf.Koanf, opts *ConfigOpts) {
|
|
||||||
cFileFailures := 0
|
|
||||||
for _, c := range configFiles {
|
|
||||||
opts.ConfigFilePath = c
|
|
||||||
data, err := fetcher.Fetch(c)
|
|
||||||
if err != nil {
|
|
||||||
cFileFailures++
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if data != nil {
|
|
||||||
if err := k.Load(rawbytes.Provider(data), yaml.Parser()); err == nil {
|
|
||||||
break
|
|
||||||
} else {
|
|
||||||
logging.ExitWithMSG(fmt.Sprintf("error loading config from file %s: %v", c, err), 1, &opts.Logger)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if cFileFailures == len(configFiles) {
|
|
||||||
logging.ExitWithMSG("Could not find any valid local config file", 1, nil)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func setTerminalEnv() {
|
func setTerminalEnv() {
|
||||||
if isatty.IsTerminal(os.Stdout.Fd()) || isatty.IsCygwinTerminal(os.Stdout.Fd()) {
|
if isatty.IsTerminal(os.Stdout.Fd()) || isatty.IsCygwinTerminal(os.Stdout.Fd()) {
|
||||||
os.Setenv("BACKY_TERM", "enabled")
|
os.Setenv("BACKY_TERM", "enabled")
|
||||||
@ -203,7 +200,7 @@ func setTerminalEnv() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func validateExecCommandsFromCLI(k *koanf.Koanf, opts *ConfigOpts) {
|
func validateCommands(k *koanf.Koanf, opts *ConfigOpts) {
|
||||||
for _, c := range opts.executeCmds {
|
for _, c := range opts.executeCmds {
|
||||||
if !k.Exists(getCmdFromConfig(c)) {
|
if !k.Exists(getCmdFromConfig(c)) {
|
||||||
logging.ExitWithMSG(fmt.Sprintf("command %s is not in config file %s", c, opts.ConfigFilePath), 1, nil)
|
logging.ExitWithMSG(fmt.Sprintf("command %s is not in config file %s", c, opts.ConfigFilePath), 1, nil)
|
||||||
@ -241,7 +238,7 @@ func setupLogger(opts *ConfigOpts) zerolog.Logger {
|
|||||||
|
|
||||||
func unmarshalConfig(k *koanf.Koanf, key string, target interface{}, log zerolog.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 {
|
if err := k.UnmarshalWithConf(key, target, koanf.UnmarshalConf{Tag: "yaml"}); err != nil {
|
||||||
logging.ExitWithMSG(fmt.Sprintf("error unmarshaling key %s into struct: %v", key, err), 1, &log)
|
logging.ExitWithMSG(fmt.Sprintf("error unmarshalling key %s into struct: %v", key, err), 1, &log)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -307,10 +304,9 @@ func loadCommandLists(opts *ConfigOpts, backyKoanf *koanf.Koanf) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if backyKoanf.Exists("cmdLists") {
|
if backyKoanf.Exists("cmdLists") {
|
||||||
|
unmarshalConfig(backyKoanf, "cmdLists", &opts.CmdConfigLists, opts.Logger)
|
||||||
if backyKoanf.Exists("cmdLists.file") {
|
if backyKoanf.Exists("cmdLists.file") {
|
||||||
loadCmdListsFile(backyKoanf, listsConfig, opts)
|
loadCmdListsFile(backyKoanf, listsConfig, opts)
|
||||||
} else {
|
|
||||||
unmarshalConfig(backyKoanf, "cmdLists", &opts.CmdConfigLists, opts.Logger)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -370,7 +366,7 @@ func loadCmdListsFile(backyKoanf *koanf.Koanf, listsConfig *koanf.Koanf, opts *C
|
|||||||
|
|
||||||
data, err := fetcher.Fetch(opts.CmdListFile)
|
data, err := fetcher.Fetch(opts.CmdListFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logging.ExitWithMSG(generateFileFetchErrorString(opts.CmdListFile, "list config", err), 1, 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 {
|
if err := listsConfig.Load(rawbytes.Provider(data), yaml.Parser()); err != nil {
|
||||||
@ -382,10 +378,6 @@ func loadCmdListsFile(backyKoanf *koanf.Koanf, listsConfig *koanf.Koanf, opts *C
|
|||||||
opts.Logger.Info().Str("using lists config file", opts.CmdListFile).Send()
|
opts.Logger.Info().Str("using lists config file", opts.CmdListFile).Send()
|
||||||
}
|
}
|
||||||
|
|
||||||
func generateFileFetchErrorString(file, fileType string, err error) string {
|
|
||||||
return fmt.Sprintf("Could not fetch %s file %s: %v", file, fileType, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func validateCommandLists(opts *ConfigOpts) {
|
func validateCommandLists(opts *ConfigOpts) {
|
||||||
var cmdNotFoundSliceErr []error
|
var cmdNotFoundSliceErr []error
|
||||||
for cmdListName, cmdList := range opts.CmdConfigLists {
|
for cmdListName, cmdList := range opts.CmdConfigLists {
|
||||||
@ -463,7 +455,7 @@ func (opts *ConfigOpts) setupVault() error {
|
|||||||
|
|
||||||
unmarshalErr := opts.koanf.UnmarshalWithConf("vault.keys", &opts.VaultKeys, koanf.UnmarshalConf{Tag: "yaml"})
|
unmarshalErr := opts.koanf.UnmarshalWithConf("vault.keys", &opts.VaultKeys, koanf.UnmarshalConf{Tag: "yaml"})
|
||||||
if unmarshalErr != nil {
|
if unmarshalErr != nil {
|
||||||
logging.ExitWithMSG(fmt.Sprintf("error unmarshaling vault.keys into struct: %v", unmarshalErr), 1, &opts.Logger)
|
logging.ExitWithMSG(fmt.Sprintf("error unmarshalling vault.keys into struct: %v", unmarshalErr), 1, &opts.Logger)
|
||||||
}
|
}
|
||||||
|
|
||||||
opts.vaultClient = client
|
opts.vaultClient = client
|
||||||
@ -573,10 +565,6 @@ func processCmds(opts *ConfigOpts) error {
|
|||||||
cmd.RemoteHost.HostName = host.HostName
|
cmd.RemoteHost.HostName = host.HostName
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
opts.Logger.Info().Msgf("adding host %s to host list", *cmd.Host)
|
|
||||||
if opts.Hosts == nil {
|
|
||||||
opts.Hosts = make(map[string]*Host)
|
|
||||||
}
|
|
||||||
opts.Hosts[*cmd.Host] = &Host{Host: *cmd.Host}
|
opts.Hosts[*cmd.Host] = &Host{Host: *cmd.Host}
|
||||||
cmd.RemoteHost = &Host{Host: *cmd.Host}
|
cmd.RemoteHost = &Host{Host: *cmd.Host}
|
||||||
}
|
}
|
||||||
@ -589,11 +577,10 @@ func processCmds(opts *ConfigOpts) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
cmd.Dir = &cmdDir
|
cmd.Dir = &cmdDir
|
||||||
} else {
|
|
||||||
cmd.Dir = &opts.ConfigDir
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Parse package commands
|
||||||
if cmd.Type == PackageCT {
|
if cmd.Type == PackageCT {
|
||||||
if cmd.PackageManager == "" {
|
if cmd.PackageManager == "" {
|
||||||
return fmt.Errorf("package manager is required for package command %s", cmd.PackageName)
|
return fmt.Errorf("package manager is required for package command %s", cmd.PackageName)
|
||||||
@ -625,29 +612,19 @@ func processCmds(opts *ConfigOpts) error {
|
|||||||
return fmt.Errorf("username is required for user command %s", cmd.Name)
|
return fmt.Errorf("username is required for user command %s", cmd.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
err := detectOSType(cmd, opts)
|
detectOSType(cmd, opts)
|
||||||
if err != nil {
|
var err error
|
||||||
opts.Logger.Info().Err(err).Str("command", cmdName).Send()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate the operation
|
// Validate the operation
|
||||||
switch cmd.UserOperation {
|
switch cmd.UserOperation {
|
||||||
case "add", "remove", "modify", "checkIfExists", "delete", "password":
|
case "add", "remove", "modify", "checkIfExists", "delete", "password":
|
||||||
cmd.userMan, err = usermanager.NewUserManager(cmd.OS)
|
cmd.userMan, err = usermanager.NewUserManager(cmd.OS)
|
||||||
if cmd.UserOperation == "password" {
|
|
||||||
cmd.UserPassword = expandExternalConfigDirectives(cmd.UserPassword, opts)
|
|
||||||
}
|
|
||||||
if cmd.Host != nil {
|
if cmd.Host != nil {
|
||||||
host, ok := opts.Hosts[*cmd.Host]
|
host, ok := opts.Hosts[*cmd.Host]
|
||||||
if ok {
|
if ok {
|
||||||
cmd.userMan, err = usermanager.NewUserManager(host.OS)
|
cmd.userMan, err = usermanager.NewUserManager(host.OS)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for indx, key := range cmd.UserSshPubKeys {
|
|
||||||
opts.Logger.Debug().Msg("adding SSH Keys")
|
|
||||||
key = expandExternalConfigDirectives(key, opts)
|
|
||||||
cmd.UserSshPubKeys[indx] = key
|
|
||||||
}
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -679,8 +656,14 @@ func processCmds(opts *ConfigOpts) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// processHooks evaluates if hooks are valid Commands
|
||||||
|
//
|
||||||
|
// The cmd.hookRefs[hookType] is created with any hooks found.
|
||||||
|
//
|
||||||
|
// Returns an error, if any, if the hook command is not found
|
||||||
func processHooks(cmd *Command, hooks []string, opts *ConfigOpts, hookType string) error {
|
func processHooks(cmd *Command, hooks []string, opts *ConfigOpts, hookType string) error {
|
||||||
|
|
||||||
|
// initialize hook type
|
||||||
var hookCmdFound bool
|
var hookCmdFound bool
|
||||||
cmd.hookRefs = map[string]map[string]*Command{}
|
cmd.hookRefs = map[string]map[string]*Command{}
|
||||||
cmd.hookRefs[hookType] = map[string]*Command{}
|
cmd.hookRefs[hookType] = map[string]*Command{}
|
||||||
@ -708,14 +691,11 @@ func processHooks(cmd *Command, hooks []string, opts *ConfigOpts, hookType strin
|
|||||||
|
|
||||||
func detectOSType(cmd *Command, opts *ConfigOpts) error {
|
func detectOSType(cmd *Command, opts *ConfigOpts) error {
|
||||||
if cmd.Host == nil {
|
if cmd.Host == nil {
|
||||||
if runtime.GOOS == "linux" {
|
if runtime.GOOS == "linux" { // also can be specified to FreeBSD
|
||||||
cmd.OS = "linux"
|
cmd.OS = "linux"
|
||||||
opts.Logger.Info().Msg("Unix/Linux type OS detected")
|
opts.Logger.Info().Msg("Unix/Linux type OS detected")
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
return fmt.Errorf("using an os that is not yet supported for user commands")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
host, ok := opts.Hosts[*cmd.Host]
|
host, ok := opts.Hosts[*cmd.Host]
|
||||||
if ok {
|
if ok {
|
||||||
if host.OS != "" {
|
if host.OS != "" {
|
||||||
|
@ -71,8 +71,9 @@ func (opts *ConfigOpts) SetupNotify() {
|
|||||||
opts.Logger.Info().Str("list", confName).Err(fmt.Errorf("error: configuring matrix id %s failed during setup: %w", id, mtrxErr))
|
opts.Logger.Info().Str("list", confName).Err(fmt.Errorf("error: configuring matrix id %s failed during setup: %w", id, mtrxErr))
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
// append the services
|
||||||
services = append(services, mtrxConf)
|
services = append(services, mtrxConf)
|
||||||
|
// service is not recognized
|
||||||
default:
|
default:
|
||||||
opts.Logger.Info().Err(fmt.Errorf("id %s not found", id)).Str("list", confName).Send()
|
opts.Logger.Info().Err(fmt.Errorf("id %s not found", id)).Str("list", confName).Send()
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,6 @@ import (
|
|||||||
|
|
||||||
"github.com/kevinburke/ssh_config"
|
"github.com/kevinburke/ssh_config"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/pkg/sftp"
|
|
||||||
"github.com/rs/zerolog"
|
"github.com/rs/zerolog"
|
||||||
"golang.org/x/crypto/ssh"
|
"golang.org/x/crypto/ssh"
|
||||||
"golang.org/x/crypto/ssh/knownhosts"
|
"golang.org/x/crypto/ssh/knownhosts"
|
||||||
@ -570,57 +569,6 @@ func (command *Command) RunCmdSSH(cmdCtxLogger zerolog.Logger, opts *ConfigOpts)
|
|||||||
if err := commandSession.Run(ArgsStr); err != nil {
|
if err := commandSession.Run(ArgsStr); err != nil {
|
||||||
return collectOutput(&cmdOutBuf, command.Name, cmdCtxLogger, command.OutputToLog), fmt.Errorf("error running command: %w", err)
|
return collectOutput(&cmdOutBuf, command.Name, cmdCtxLogger, command.OutputToLog), fmt.Errorf("error running command: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if command.Type == UserCT && command.UserOperation == "add" {
|
|
||||||
if command.UserSshPubKeys != nil {
|
|
||||||
var (
|
|
||||||
f *sftp.File
|
|
||||||
err error
|
|
||||||
userHome []byte
|
|
||||||
client *sftp.Client
|
|
||||||
)
|
|
||||||
|
|
||||||
cmdCtxLogger.Info().Msg("adding SSH Keys")
|
|
||||||
|
|
||||||
commandSession, _ = command.RemoteHost.createSSHSession(opts)
|
|
||||||
userHome, err = commandSession.CombinedOutput(fmt.Sprintf("grep \"%s\" /etc/passwd | cut -d: -f6", command.Username))
|
|
||||||
if err != nil {
|
|
||||||
return collectOutput(&cmdOutBuf, command.Name, cmdCtxLogger, command.OutputToLog), fmt.Errorf("error finding user home from /etc/passwd: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
command.UserHome = strings.TrimSpace(string(userHome))
|
|
||||||
userSshDir := fmt.Sprintf("%s/.ssh", command.UserHome)
|
|
||||||
client, err = sftp.NewClient(command.RemoteHost.SshClient)
|
|
||||||
if err != nil {
|
|
||||||
return collectOutput(&cmdOutBuf, command.Name, cmdCtxLogger, command.OutputToLog), fmt.Errorf("error creating sftp client: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
client.MkdirAll(userSshDir)
|
|
||||||
_, err = client.Create(fmt.Sprintf("%s/authorized_keys", userSshDir))
|
|
||||||
if err != nil {
|
|
||||||
return collectOutput(&cmdOutBuf, command.Name, cmdCtxLogger, command.OutputToLog), fmt.Errorf("error opening file %s/authorized_keys: %v", userSshDir, err)
|
|
||||||
}
|
|
||||||
f, err = client.OpenFile(fmt.Sprintf("%s/authorized_keys", userSshDir), os.O_APPEND|os.O_CREATE|os.O_WRONLY)
|
|
||||||
if err != nil {
|
|
||||||
return collectOutput(&cmdOutBuf, command.Name, cmdCtxLogger, command.OutputToLog), fmt.Errorf("error opening file %s/authorized_keys: %v", userSshDir, err)
|
|
||||||
}
|
|
||||||
defer f.Close()
|
|
||||||
for _, k := range command.UserSshPubKeys {
|
|
||||||
buf := bytes.NewBufferString(k)
|
|
||||||
cmdCtxLogger.Info().Str("key", k).Msg("adding SSH key")
|
|
||||||
if _, err := f.ReadFrom(buf); err != nil {
|
|
||||||
return collectOutput(&cmdOutBuf, command.Name, cmdCtxLogger, command.OutputToLog), fmt.Errorf("error adding to authorized keys: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
commandSession, _ = command.RemoteHost.createSSHSession(opts)
|
|
||||||
_, err = commandSession.CombinedOutput(fmt.Sprintf("chown -R %s:%s %s", command.Username, command.Username, userHome))
|
|
||||||
if err != nil {
|
|
||||||
return collectOutput(&cmdOutBuf, command.Name, cmdCtxLogger, command.OutputToLog), err
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return collectOutput(&cmdOutBuf, command.Name, cmdCtxLogger, command.OutputToLog), nil
|
return collectOutput(&cmdOutBuf, command.Name, cmdCtxLogger, command.OutputToLog), nil
|
||||||
@ -674,7 +622,7 @@ func (command *Command) runScript(session *ssh.Session, cmdCtxLogger zerolog.Log
|
|||||||
return collectOutput(outputBuf, command.Name, cmdCtxLogger, true), 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, command.OutputToLog), nil
|
return collectOutput(outputBuf, command.Name, cmdCtxLogger, command.GetOutput), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// runScriptFile handles the execution of script files.
|
// runScriptFile handles the execution of script files.
|
||||||
|
@ -51,30 +51,44 @@ type (
|
|||||||
Command struct {
|
Command struct {
|
||||||
Name string `yaml:"name,omitempty"`
|
Name string `yaml:"name,omitempty"`
|
||||||
|
|
||||||
|
// command to run
|
||||||
Cmd string `yaml:"cmd"`
|
Cmd string `yaml:"cmd"`
|
||||||
|
|
||||||
// See CommandType enum further down the page for acceptable values
|
// See CommandType enum further down the page for acceptable values
|
||||||
Type CommandType `yaml:"type,omitempty"`
|
Type CommandType `yaml:"type,omitempty"`
|
||||||
|
|
||||||
|
// host on which to run cmd
|
||||||
Host *string `yaml:"host,omitempty"`
|
Host *string `yaml:"host,omitempty"`
|
||||||
|
|
||||||
|
// Hooks are for running commands on certain events
|
||||||
Hooks *Hooks `yaml:"hooks,omitempty"`
|
Hooks *Hooks `yaml:"hooks,omitempty"`
|
||||||
|
|
||||||
|
// hook refs are internal references of commands for each hook type
|
||||||
hookRefs map[string]map[string]*Command
|
hookRefs map[string]map[string]*Command
|
||||||
|
|
||||||
|
// Shell specifies which shell to run the command in, if any.
|
||||||
Shell string `yaml:"shell,omitempty"`
|
Shell string `yaml:"shell,omitempty"`
|
||||||
|
|
||||||
RemoteHost *Host `yaml:"-"`
|
RemoteHost *Host `yaml:"-"`
|
||||||
|
|
||||||
|
// Args is an array that holds the arguments to cmd
|
||||||
Args []string `yaml:"args,omitempty"`
|
Args []string `yaml:"args,omitempty"`
|
||||||
|
|
||||||
|
/*
|
||||||
|
Dir specifies a directory in which to run the command.
|
||||||
|
*/
|
||||||
Dir *string `yaml:"dir,omitempty"`
|
Dir *string `yaml:"dir,omitempty"`
|
||||||
|
|
||||||
|
// Env points to a file containing env variables to be used with the command
|
||||||
Env string `yaml:"env,omitempty"`
|
Env string `yaml:"env,omitempty"`
|
||||||
|
|
||||||
|
// Environment holds env variables to be used with the command
|
||||||
Environment []string `yaml:"environment,omitempty"`
|
Environment []string `yaml:"environment,omitempty"`
|
||||||
|
|
||||||
GetOutputInList bool `yaml:"getOutputInList,omitempty"`
|
// Output determines if output is requested.
|
||||||
|
//
|
||||||
|
// Only for when command is in a list.
|
||||||
|
GetOutput bool `yaml:"getOutput,omitempty"`
|
||||||
|
|
||||||
ScriptEnvFile string `yaml:"scriptEnvFile"`
|
ScriptEnvFile string `yaml:"scriptEnvFile"`
|
||||||
|
|
||||||
@ -88,8 +102,10 @@ type (
|
|||||||
|
|
||||||
PackageName string `yaml:"packageName,omitempty"`
|
PackageName string `yaml:"packageName,omitempty"`
|
||||||
|
|
||||||
|
// Version specifies the desired version for package execution
|
||||||
PackageVersion string `yaml:"packageVersion,omitempty"`
|
PackageVersion string `yaml:"packageVersion,omitempty"`
|
||||||
|
|
||||||
|
// PackageOperation specifies the action for package-related commands (e.g., "install" or "remove")
|
||||||
PackageOperation PackageOperation `yaml:"packageOperation,omitempty"`
|
PackageOperation PackageOperation `yaml:"packageOperation,omitempty"`
|
||||||
|
|
||||||
pkgMan pkgman.PackageManager
|
pkgMan pkgman.PackageManager
|
||||||
@ -97,35 +113,42 @@ type (
|
|||||||
packageCmdSet bool
|
packageCmdSet bool
|
||||||
// END PACKAGE COMMAND FIELDS
|
// END PACKAGE COMMAND FIELDS
|
||||||
|
|
||||||
|
// RemoteSource specifies a URL to fetch the command or configuration remotely
|
||||||
RemoteSource string `yaml:"remoteSource,omitempty"`
|
RemoteSource string `yaml:"remoteSource,omitempty"`
|
||||||
|
|
||||||
|
// FetchBeforeExecution determines if the remoteSource should be fetched before running
|
||||||
FetchBeforeExecution bool `yaml:"fetchBeforeExecution,omitempty"`
|
FetchBeforeExecution bool `yaml:"fetchBeforeExecution,omitempty"`
|
||||||
|
|
||||||
Fetcher remotefetcher.RemoteFetcher
|
Fetcher remotefetcher.RemoteFetcher
|
||||||
|
|
||||||
// BEGIN USER COMMAND FIELDS
|
// BEGIN USER COMMAND FIELDS
|
||||||
|
|
||||||
|
// Username specifies the username for user creation or related operations
|
||||||
Username string `yaml:"userName,omitempty"`
|
Username string `yaml:"userName,omitempty"`
|
||||||
|
|
||||||
UserID string `yaml:"userID,omitempty"`
|
UserID string `yaml:"userID,omitempty"`
|
||||||
|
|
||||||
|
// UserGroups specifies the groups to add the user to
|
||||||
UserGroups []string `yaml:"userGroups,omitempty"`
|
UserGroups []string `yaml:"userGroups,omitempty"`
|
||||||
|
|
||||||
|
// UserHome specifies the home directory for the user
|
||||||
UserHome string `yaml:"userHome,omitempty"`
|
UserHome string `yaml:"userHome,omitempty"`
|
||||||
|
|
||||||
|
// UserShell specifies the shell for the user
|
||||||
UserShell string `yaml:"userShell,omitempty"`
|
UserShell string `yaml:"userShell,omitempty"`
|
||||||
|
|
||||||
|
// SystemUser specifies whether the user is a system account
|
||||||
SystemUser bool `yaml:"systemUser,omitempty"`
|
SystemUser bool `yaml:"systemUser,omitempty"`
|
||||||
|
|
||||||
|
// UserPassword specifies the password for the user (can be file: or plain text)
|
||||||
UserPassword string `yaml:"userPassword,omitempty"`
|
UserPassword string `yaml:"userPassword,omitempty"`
|
||||||
|
|
||||||
UserSshPubKeys []string `yaml:"userSshPubKeys,omitempty"`
|
|
||||||
|
|
||||||
userMan usermanager.UserManager
|
userMan usermanager.UserManager
|
||||||
|
|
||||||
// OS for the command, only used when type is user
|
// OS for the command, only used when type is user
|
||||||
OS string `yaml:"OS,omitempty"`
|
OS string `yaml:"OS,omitempty"`
|
||||||
|
|
||||||
|
// UserOperation specifies the action for user-related commands (e.g., "create" or "remove")
|
||||||
UserOperation string `yaml:"userOperation,omitempty"`
|
UserOperation string `yaml:"userOperation,omitempty"`
|
||||||
|
|
||||||
userCmdSet bool
|
userCmdSet bool
|
||||||
|
@ -247,6 +247,7 @@ func (opts *ConfigOpts) loadEnv() {
|
|||||||
opts.backyEnv = backyEnv
|
opts.backyEnv = backyEnv
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// expandEnvVars expands environment variables with the env used in the config
|
||||||
func expandEnvVars(backyEnv map[string]string, envVars []string) {
|
func expandEnvVars(backyEnv map[string]string, envVars []string) {
|
||||||
|
|
||||||
env := func(name string) string {
|
env := func(name string) string {
|
||||||
@ -260,10 +261,10 @@ func expandEnvVars(backyEnv map[string]string, envVars []string) {
|
|||||||
|
|
||||||
// parse env variables using new macros
|
// parse env variables using new macros
|
||||||
for indx, v := range envVars {
|
for indx, v := range envVars {
|
||||||
if strings.HasPrefix(v, externDirectiveStart) && strings.HasSuffix(v, externDirectiveEnd) {
|
if strings.HasPrefix(v, macroStart) && strings.HasSuffix(v, macroEnd) {
|
||||||
if strings.HasPrefix(v, envExternDirectiveStart) {
|
if strings.HasPrefix(v, envMacroStart) {
|
||||||
v = strings.TrimPrefix(v, envExternDirectiveStart)
|
v = strings.TrimPrefix(v, envMacroStart)
|
||||||
v = strings.TrimRight(v, externDirectiveEnd)
|
v = strings.TrimRight(v, macroEnd)
|
||||||
out, _ := shell.Expand(v, env)
|
out, _ := shell.Expand(v, env)
|
||||||
envVars[indx] = out
|
envVars[indx] = out
|
||||||
}
|
}
|
||||||
@ -323,7 +324,7 @@ func parsePackageVersion(output string, cmdCtxLogger zerolog.Logger, command *Co
|
|||||||
// println(output)
|
// println(output)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cmdCtxLogger.Error().Err(err).Str("package", command.PackageName).Msg("Error parsing package version output")
|
cmdCtxLogger.Error().Err(err).Str("package", command.PackageName).Msg("Error parsing package version output")
|
||||||
return collectOutput(&cmdOutBuf, command.Name, cmdCtxLogger, command.OutputToLog), err
|
return collectOutput(&cmdOutBuf, command.Name, cmdCtxLogger, command.GetOutput), err
|
||||||
}
|
}
|
||||||
|
|
||||||
cmdCtxLogger.Info().
|
cmdCtxLogger.Info().
|
||||||
@ -348,39 +349,3 @@ func parsePackageVersion(output string, cmdCtxLogger zerolog.Logger, command *Co
|
|||||||
}
|
}
|
||||||
return collectOutput(&cmdOutBuf, command.Name, cmdCtxLogger, false), err
|
return collectOutput(&cmdOutBuf, command.Name, cmdCtxLogger, false), err
|
||||||
}
|
}
|
||||||
|
|
||||||
func expandExternalConfigDirectives(key string, opts *ConfigOpts) string {
|
|
||||||
if !(strings.HasPrefix(key, externDirectiveStart) && strings.HasSuffix(key, externDirectiveEnd)) {
|
|
||||||
return key
|
|
||||||
}
|
|
||||||
opts.Logger.Info().Str("expanding external key", key).Send()
|
|
||||||
if strings.HasPrefix(key, envExternDirectiveStart) {
|
|
||||||
key = strings.TrimPrefix(key, envExternDirectiveStart)
|
|
||||||
key = strings.TrimSuffix(key, externDirectiveEnd)
|
|
||||||
return os.Getenv(key)
|
|
||||||
}
|
|
||||||
if strings.HasPrefix(key, externFileDirectiveStart) {
|
|
||||||
var err error
|
|
||||||
var keyValue []byte
|
|
||||||
key = strings.TrimPrefix(key, externFileDirectiveStart)
|
|
||||||
key = strings.TrimSuffix(key, externDirectiveEnd)
|
|
||||||
key, err = getFullPathWithHomeDir(key)
|
|
||||||
if err != nil {
|
|
||||||
opts.Logger.Err(err).Send()
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
keyValue, err = os.ReadFile(key)
|
|
||||||
if err != nil {
|
|
||||||
opts.Logger.Err(err).Send()
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
key = string(keyValue)
|
|
||||||
}
|
|
||||||
if strings.HasPrefix(key, vaultExternDirectiveStart) {
|
|
||||||
key = strings.TrimPrefix(key, vaultExternDirectiveStart)
|
|
||||||
key = strings.TrimSuffix(key, externDirectiveEnd)
|
|
||||||
key = GetVaultKey(key, opts, opts.Logger)
|
|
||||||
}
|
|
||||||
|
|
||||||
return key
|
|
||||||
}
|
|
||||||
|
@ -29,7 +29,7 @@ func NewRemoteFetcher(source string, cache *Cache, options ...FetcherOption) (Re
|
|||||||
option(&config)
|
option(&config)
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithFileType was not called. yaml is the default file type
|
// If FileType is empty (i.e. WithFileType was not called), yaml is the default file type
|
||||||
if strings.TrimSpace(config.FileType) == "" {
|
if strings.TrimSpace(config.FileType) == "" {
|
||||||
config.FileType = "yaml"
|
config.FileType = "yaml"
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,7 @@ func (l *LocalFetcher) Fetch(source string) ([]byte, error) {
|
|||||||
if l.config.IgnoreFileNotFound {
|
if l.config.IgnoreFileNotFound {
|
||||||
return nil, ErrIgnoreFileNotFound
|
return nil, ErrIgnoreFileNotFound
|
||||||
}
|
}
|
||||||
return nil, err
|
return nil, nil
|
||||||
}
|
}
|
||||||
file, err := os.Open(source)
|
file, err := os.Open(source)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -44,8 +44,9 @@ func NewS3Fetcher(endpoint string, options ...FetcherOption) (*S3Fetcher, error)
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
s3Endpoint := os.Getenv("S3_ENDPOINT")
|
s3Endpoint := os.Getenv("S3_ENDPOINT")
|
||||||
creds, err := getS3Credentials(os.Getenv("AWS_PROFILE"), s3Endpoint, cfg.HTTPClient)
|
creds, err := getS3Credentials("default", s3Endpoint, cfg.HTTPClient)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
println(err.Error())
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -132,7 +133,7 @@ func getS3Credentials(profile, host string, httpClient *http.Client) (*credentia
|
|||||||
if hdirErr != nil {
|
if hdirErr != nil {
|
||||||
return nil, hdirErr
|
return nil, hdirErr
|
||||||
}
|
}
|
||||||
s3Creds := credentials.NewFileAWSCredentials(path.Join(homeDir, ".aws", "credentials"), profile)
|
s3Creds := credentials.NewFileAWSCredentials(path.Join(homeDir, ".aws", "credentials"), "default")
|
||||||
credVals, credErr := s3Creds.GetWithContext(&credentials.CredContext{Endpoint: host, Client: httpClient})
|
credVals, credErr := s3Creds.GetWithContext(&credentials.CredContext{Endpoint: host, Client: httpClient})
|
||||||
if credErr != nil {
|
if credErr != nil {
|
||||||
return nil, credErr
|
return nil, credErr
|
||||||
|
2
release
2
release
@ -1,7 +1,7 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
set -eou pipefail
|
set -eou pipefail
|
||||||
go generate ./...
|
go generate ./...
|
||||||
CURRENT_TAG="$(go run backy.go version -V)"
|
export CURRENT_TAG="$(go run backy.go version -V)"
|
||||||
goreleaser -f .goreleaser/github.yml check
|
goreleaser -f .goreleaser/github.yml check
|
||||||
goreleaser -f .goreleaser/gitea.yml check
|
goreleaser -f .goreleaser/gitea.yml check
|
||||||
changie batch $CURRENT_TAG
|
changie batch $CURRENT_TAG
|
||||||
|
1
tests/.gitignore
vendored
1
tests/.gitignore
vendored
@ -1 +0,0 @@
|
|||||||
private
|
|
@ -1,17 +0,0 @@
|
|||||||
commands:
|
|
||||||
echoTestFail:
|
|
||||||
cmd: ech
|
|
||||||
shell: bash
|
|
||||||
Args: hello world
|
|
||||||
hooks:
|
|
||||||
error:
|
|
||||||
- errorCmd
|
|
||||||
|
|
||||||
errorCmd:
|
|
||||||
name: get docker version
|
|
||||||
cmd: docker
|
|
||||||
getOutput: true
|
|
||||||
outputToLog: true
|
|
||||||
Args:
|
|
||||||
- "-v"
|
|
||||||
host: email-svr
|
|
@ -1,14 +0,0 @@
|
|||||||
commands:
|
|
||||||
echoTestFail:
|
|
||||||
cmd: ech
|
|
||||||
shell: bash
|
|
||||||
Args: hello world
|
|
||||||
hooks:
|
|
||||||
error:
|
|
||||||
- errorCm #
|
|
||||||
|
|
||||||
errorCmd:
|
|
||||||
name: get docker version
|
|
||||||
cmd: docker
|
|
||||||
Args:
|
|
||||||
- "-v"
|
|
@ -1,16 +0,0 @@
|
|||||||
commands:
|
|
||||||
echoTestSuccess:
|
|
||||||
cmd: echo
|
|
||||||
shell: bash
|
|
||||||
Args: hello world
|
|
||||||
hooks:
|
|
||||||
success:
|
|
||||||
- successCmd
|
|
||||||
|
|
||||||
errorCmd:
|
|
||||||
name: get docker version
|
|
||||||
cmd: docker
|
|
||||||
getOutput: true
|
|
||||||
outputToLog: true
|
|
||||||
Args:
|
|
||||||
- "-v"
|
|
18
tests/backy.yaml
Normal file
18
tests/backy.yaml
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
commands:
|
||||||
|
echoTestPass:
|
||||||
|
cmd: echo
|
||||||
|
shell: bash
|
||||||
|
Args: hello world
|
||||||
|
|
||||||
|
runRemoteShellScriptSuccess:
|
||||||
|
cmd:
|
||||||
|
|
||||||
|
|
||||||
|
packageCommandSuccess:
|
||||||
|
packageName: docker-ce
|
||||||
|
Args:
|
||||||
|
- docker-ce-cli
|
||||||
|
packageManager: apt
|
||||||
|
packageOperation: install
|
||||||
|
|
||||||
|
|
Loading…
x
Reference in New Issue
Block a user