Compare commits

..

9 Commits

Author SHA1 Message Date
Bo-Yi Wu 67a2631616 docs: improve clarity of Usage message in main.go
- Modify the `Usage` message in main.go

Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>
2023-04-13 09:48:46 +08:00
Alexey Smirnov fdc4c8de43 fix: example with secrets (#178) 2023-04-13 09:39:43 +08:00
Bo-Yi Wu 49542638c6 refactor: set default environment variables format in plugin (#253)
- Add `INPUT_ENVS_FORMAT` to the list of environment variables in `main.go`
- Define `envsFormat` variable in `plugin.go`
- Add default value for `Config.EnvsFormat` in `plugin.go` `Exec()` function

fix https://github.com/appleboy/drone-ssh/pull/235
2023-04-13 09:32:31 +08:00
Vladimir Sigalkin 6464d9999f chore: flexible configuration of environment value transfer (#235)
**Reason:**
I have to use drone-ssh to work with Windows SSH. Initially, drone-ssh is written so that it transmits environment variables through the `export` command. Which makes it unsuitable for working with Power Shell.

**Solution:**
I have added a new option to configure environment variable commands formatting, with default value: `export {NAME}={VALUE}`. When I use drone-ssh with PowerShell I set this option like this: `$env:{NAME} = {VALUE}`.
2023-04-13 09:13:07 +08:00
Bo-Yi Wu 4aabfc90dd chore: upgrade Dependencies to Latest Versions
- Upgrade easyssh-proxy from v1.3.9 to v1.3.10
- Upgrade testify from v1.8.1 to v1.8.2
- Upgrade crypto from v0.7.0 to v0.8.0
- Upgrade sys from v0.6.0 to v0.7.0

Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>
2023-04-13 09:09:37 +08:00
Bo-Yi Wu b9cfca60e9 refactor: standardize environment variables naming conventions (#252)
- Modify the `EnvVars` slice to include `INPUT_` prefix when needed
- Remove unused environment variables from the `EnvVars` slice
- Update the `EnvVars` slice to include `PROXY_` prefix when needed
- Modify the `EnvVars` slice to include `PLUGIN_SSH_CIPHERS` as `PROXY_SSH_CIPHERS`
- Modify the `EnvVars` slice to include `PLUGIN_PROXY_USE_INSECURE_CIPHER` as `PROXY_SSH_USE_INSECURE_CIPHER`
- Modify the `EnvVars` slice to include `PLUGIN_PROXY_FINGERPRINT` as `PROXY_SSH_FINGERPRINT`
- Remove the environment variable `PROXY_HOST`
- Modify the `EnvVars` slice to include `PLUGIN_DEBUG` as `INPUT_DEBUG`

* https://github.com/appleboy/ssh-action/issues/53#issuecomment-1184700432
* https://github.com/appleboy/ssh-action/issues/32#issuecomment-1184700363
* https://github.com/appleboy/ssh-action/issues/213
2023-04-13 09:08:12 +08:00
Bo-Yi Wu a8d30dc133 feat: refactor code for parallel execution on multiple hosts (#249)
- Add `trimValues` function for cleaning up slice values
- Remove unused `wg.Done()` call
- Modify `Exec` function to launch goroutines for each host in `Config.Host`
- Add test for `ScriptStop` with multiple hosts and sync mode

refer to: https://github.com/appleboy/ssh-action/issues/233
2023-04-04 16:56:50 +08:00
Bo-Yi.Wu f733bb5ebf chore: remove Hadolint configuration file
- Delete the `.hadolint.yaml` file.

Signed-off-by: Bo-Yi.Wu <appleboy.tw@gmail.com>
2023-04-04 15:39:10 +08:00
Bo-Yi.Wu b0ddeb9413 chore: update container security measures
- Add Open Container Initiative labels for image source, description, and licenses
- Update `apk` command to include specific version of `ca-certificates`

Signed-off-by: Bo-Yi.Wu <appleboy.tw@gmail.com>
2023-04-04 15:38:42 +08:00
8 changed files with 149 additions and 77 deletions
-2
View File
@@ -1,2 +0,0 @@
ignored:
- DL3018
+5 -4
View File
@@ -130,18 +130,19 @@ Example configuration for exporting custom secrets:
- name: ssh commands
image: ghcr.io/appleboy/drone-ssh
environment:
commit: ${DRONE_BUILD_NUMBER}
# MUST BE in UPPERCASE
COMMIT:
from_secret: commit
settings:
host: foo.com
username: root
password: 1234
port: 22
+ envs:
- aws_access_key_id
# can be in lowercase (uppercased in code)
- commit
script:
- export AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID
- echo $commit
- echo $COMMIT
```
Example configuration for stoping script after first failure:
+5 -1
View File
@@ -8,7 +8,11 @@ LABEL maintainer="Bo-Yi Wu <appleboy.tw@gmail.com>" \
org.label-schema.vendor="Bo-Yi Wu" \
org.label-schema.schema-version="1.0"
RUN apk add --no-cache ca-certificates && \
LABEL org.opencontainers.image.source=https://github.com/appleboy/drone-ssh
LABEL org.opencontainers.image.description="Execute commands on a remote host through SSH"
LABEL org.opencontainers.image.licenses=MIT
RUN apk add --no-cache ca-certificates=20220614-r4 && \
rm -rf /var/cache/apk/*
COPY release/${TARGETOS}/${TARGETARCH}/drone-ssh /bin/
+4 -4
View File
@@ -3,11 +3,11 @@ module github.com/appleboy/drone-ssh
go 1.18
require (
github.com/appleboy/easyssh-proxy v1.3.9
github.com/appleboy/easyssh-proxy v1.3.10
github.com/joho/godotenv v1.5.1
github.com/stretchr/testify v1.8.1
github.com/stretchr/testify v1.8.2
github.com/urfave/cli/v2 v2.25.1
golang.org/x/crypto v0.7.0
golang.org/x/crypto v0.8.0
)
require (
@@ -18,6 +18,6 @@ require (
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
golang.org/x/sys v0.6.0 // indirect
golang.org/x/sys v0.7.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
+9 -14
View File
@@ -1,8 +1,7 @@
github.com/ScaleFT/sshkeys v0.0.0-20200327173127-6142f742bca5/go.mod h1:gxOHeajFfvGQh/fxlC8oOKBe23xnnJTif00IFFbiT+o=
github.com/ScaleFT/sshkeys v1.2.0 h1:5BRp6rTVIhJzXT3VcUQrKgXR8zWA3sOsNeuyW15WUA8=
github.com/ScaleFT/sshkeys v1.2.0/go.mod h1:gxOHeajFfvGQh/fxlC8oOKBe23xnnJTif00IFFbiT+o=
github.com/appleboy/easyssh-proxy v1.3.9 h1:b+sVSTz+cVFvfA23HQywMMpm0s5g3gH7jYdBcQqaCQI=
github.com/appleboy/easyssh-proxy v1.3.9/go.mod h1:G1eQomBEME7NWKA3hE49s5HsT44S5fn0aBxX7k9Yjug=
github.com/appleboy/easyssh-proxy v1.3.10 h1:iriF68tlrYoxgWhS7t7Wyr0FA+hJlOem5tMfm+RDlx4=
github.com/appleboy/easyssh-proxy v1.3.10/go.mod h1:T81pu/Cxx/zf/7YXhFCFiucBa4xeQ81ci5b0PFnMRJc=
github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -20,29 +19,25 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/urfave/cli/v2 v2.25.1 h1:zw8dSP7ghX0Gmm8vugrs6q9Ku0wzweqPyshy+syu9Gw=
github.com/urfave/cli/v2 v2.25.1/go.mod h1:GHupkWPMM0M/sj1a2b4wUrWBPzazNrIjouW6fmdJLxc=
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU=
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201208171446-5f87f3452ae9/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A=
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
golang.org/x/crypto v0.8.0 h1:pd9TJtTueMTVQXzk8E2XESSMQDj/U7OUu0PqJqPXQjQ=
golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200219091948-cb0a6d8edb6c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.6.0 h1:clScbb1cHjoCkyRbWwBEUZ5H/tIFu5TAXIqaZD0Gcjw=
golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU=
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.7.0 h1:BEvjmm5fURWqcfbSKTdpkDXYBrUS1c0m8agp14W48vQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+27 -19
View File
@@ -39,12 +39,12 @@ func main() {
&cli.StringFlag{
Name: "ssh-key",
Usage: "private ssh key",
EnvVars: []string{"PLUGIN_SSH_KEY", "PLUGIN_KEY", "SSH_KEY", "KEY", "INPUT_KEY"},
EnvVars: []string{"PLUGIN_SSH_KEY", "PLUGIN_KEY", "SSH_KEY", "INPUT_KEY"},
},
&cli.StringFlag{
Name: "ssh-passphrase",
Usage: "The purpose of the passphrase is usually to encrypt the private key.",
EnvVars: []string{"PLUGIN_SSH_PASSPHRASE", "PLUGIN_PASSPHRASE", "SSH_PASSPHRASE", "PASSPHRASE", "INPUT_PASSPHRASE"},
EnvVars: []string{"PLUGIN_SSH_PASSPHRASE", "PLUGIN_PASSPHRASE", "SSH_PASSPHRASE", "INPUT_PASSPHRASE"},
},
&cli.StringFlag{
Name: "key-path",
@@ -56,68 +56,68 @@ func main() {
Name: "username",
Aliases: []string{"user", "u"},
Usage: "connect as user",
EnvVars: []string{"PLUGIN_USERNAME", "PLUGIN_USER", "SSH_USERNAME", "USERNAME", "INPUT_USERNAME"},
EnvVars: []string{"PLUGIN_USERNAME", "PLUGIN_USER", "SSH_USERNAME", "INPUT_USERNAME"},
Value: "root",
},
&cli.StringFlag{
Name: "password",
Aliases: []string{"P"},
Usage: "user password",
EnvVars: []string{"PLUGIN_PASSWORD", "SSH_PASSWORD", "PASSWORD", "INPUT_PASSWORD"},
EnvVars: []string{"PLUGIN_PASSWORD", "SSH_PASSWORD", "INPUT_PASSWORD"},
},
&cli.StringSliceFlag{
Name: "ciphers",
Usage: "The allowed cipher algorithms. If unspecified then a sensible",
EnvVars: []string{"PLUGIN_CIPHERS", "SSH_CIPHERS", "CIPHERS", "INPUT_CIPHERS"},
EnvVars: []string{"PLUGIN_CIPHERS", "SSH_CIPHERS", "INPUT_CIPHERS"},
},
&cli.BoolFlag{
Name: "useInsecureCipher",
Usage: "include more ciphers with use_insecure_cipher",
EnvVars: []string{"PLUGIN_USE_INSECURE_CIPHER", "SSH_USE_INSECURE_CIPHER", "USE_INSECURE_CIPHER", "INPUT_USE_INSECURE_CIPHER"},
EnvVars: []string{"PLUGIN_USE_INSECURE_CIPHER", "SSH_USE_INSECURE_CIPHER", "INPUT_USE_INSECURE_CIPHER"},
},
&cli.StringFlag{
Name: "fingerprint",
Usage: "fingerprint SHA256 of the host public key, default is to skip verification",
EnvVars: []string{"PLUGIN_FINGERPRINT", "SSH_FINGERPRINT", "FINGERPRINT", "INPUT_FINGERPRINT"},
EnvVars: []string{"PLUGIN_FINGERPRINT", "SSH_FINGERPRINT", "INPUT_FINGERPRINT"},
},
&cli.StringSliceFlag{
Name: "host",
Aliases: []string{"H"},
Usage: "connect to host",
EnvVars: []string{"PLUGIN_HOST", "SSH_HOST", "HOST", "INPUT_HOST"},
EnvVars: []string{"PLUGIN_HOST", "SSH_HOST", "INPUT_HOST"},
FilePath: ".host",
},
&cli.IntFlag{
Name: "port",
Aliases: []string{"p"},
Usage: "connect to port",
EnvVars: []string{"PLUGIN_PORT", "SSH_PORT", "PORT", "INPUT_PORT"},
EnvVars: []string{"PLUGIN_PORT", "SSH_PORT", "INPUT_PORT"},
Value: 22,
},
&cli.BoolFlag{
Name: "sync",
Usage: "sync mode",
EnvVars: []string{"PLUGIN_SYNC", "SYNC", "INPUT_SYNC"},
EnvVars: []string{"PLUGIN_SYNC", "INPUT_SYNC"},
},
&cli.DurationFlag{
Name: "timeout",
Aliases: []string{"t"},
Usage: "connection timeout",
EnvVars: []string{"PLUGIN_TIMEOUT", "SSH_TIMEOUT", "TIMEOUT", "INPUT_TIMEOUT"},
EnvVars: []string{"PLUGIN_TIMEOUT", "SSH_TIMEOUT", "INPUT_TIMEOUT"},
Value: 30 * time.Second,
},
&cli.DurationFlag{
Name: "command.timeout",
Aliases: []string{"T"},
Usage: "command timeout",
EnvVars: []string{"PLUGIN_COMMAND_TIMEOUT", "SSH_COMMAND_TIMEOUT", "COMMAND_TIMEOUT", "INPUT_COMMAND_TIMEOUT"},
EnvVars: []string{"PLUGIN_COMMAND_TIMEOUT", "SSH_COMMAND_TIMEOUT", "INPUT_COMMAND_TIMEOUT"},
Value: 10 * time.Minute,
},
&cli.StringSliceFlag{
Name: "script",
Aliases: []string{"s"},
Usage: "execute commands",
EnvVars: []string{"PLUGIN_SCRIPT", "SSH_SCRIPT", "SCRIPT"},
EnvVars: []string{"PLUGIN_SCRIPT", "SSH_SCRIPT"},
},
&cli.StringFlag{
Name: "script.string",
@@ -127,7 +127,7 @@ func main() {
&cli.BoolFlag{
Name: "script.stop",
Usage: "stop script after first failure",
EnvVars: []string{"PLUGIN_SCRIPT_STOP", "STOP", "INPUT_SCRIPT_STOP"},
EnvVars: []string{"PLUGIN_SCRIPT_STOP", "INPUT_SCRIPT_STOP"},
},
&cli.StringFlag{
Name: "proxy.ssh-key",
@@ -137,7 +137,7 @@ func main() {
&cli.StringFlag{
Name: "proxy.ssh-passphrase",
Usage: "The purpose of the passphrase is usually to encrypt the private key.",
EnvVars: []string{"PLUGIN_PROXY_SSH_PASSPHRASE", "PLUGIN_PROXY_PASSPHRASE", "PROXY_SSH_PASSPHRASE", "PROXY_PASSPHRASE", "INPUT_PROXY_PASSPHRASE"},
EnvVars: []string{"PLUGIN_PROXY_SSH_PASSPHRASE", "PLUGIN_PROXY_PASSPHRASE", "PROXY_SSH_PASSPHRASE", "INPUT_PROXY_PASSPHRASE"},
},
&cli.StringFlag{
Name: "proxy.key-path",
@@ -174,17 +174,17 @@ func main() {
&cli.StringSliceFlag{
Name: "proxy.ciphers",
Usage: "The allowed cipher algorithms. If unspecified then a sensible",
EnvVars: []string{"PLUGIN_PROXY_CIPHERS", "SSH_PROXY_CIPHERS", "PROXY_CIPHERS", "INPUT_PROXY_CIPHERS"},
EnvVars: []string{"PLUGIN_PROXY_CIPHERS", "PROXY_SSH_CIPHERS", "INPUT_PROXY_CIPHERS"},
},
&cli.BoolFlag{
Name: "proxy.useInsecureCipher",
Usage: "include more ciphers with use_insecure_cipher",
EnvVars: []string{"PLUGIN_PROXY_USE_INSECURE_CIPHER", "SSH_PROXY_USE_INSECURE_CIPHER", "PROXY_USE_INSECURE_CIPHER", "INPUT_PROXY_USE_INSECURE_CIPHER"},
EnvVars: []string{"PLUGIN_PROXY_USE_INSECURE_CIPHER", "PROXY_SSH_USE_INSECURE_CIPHER", "INPUT_PROXY_USE_INSECURE_CIPHER"},
},
&cli.StringFlag{
Name: "proxy.fingerprint",
Usage: "fingerprint SHA256 of the host public key, default is to skip verification",
EnvVars: []string{"PLUGIN_PROXY_FINGERPRINT", "SSH_PROXY_FINGERPRINT", "PROXY_FINGERPRINT", "INPUT_PROXY_FINGERPRINT"},
EnvVars: []string{"PLUGIN_PROXY_FINGERPRINT", "PROXY_SSH_FINGERPRINT", "PROXY_FINGERPRINT", "INPUT_PROXY_FINGERPRINT"},
},
&cli.StringSliceFlag{
Name: "envs",
@@ -194,7 +194,13 @@ func main() {
&cli.BoolFlag{
Name: "debug",
Usage: "debug mode",
EnvVars: []string{"PLUGIN_DEBUG", "DEBUG", "INPUT_DEBUG"},
EnvVars: []string{"PLUGIN_DEBUG", "INPUT_DEBUG"},
},
&cli.StringFlag{
Name: "envs.format",
Usage: "flexible configuration of environment value transfer",
EnvVars: []string{"PLUGIN_ENVS_FORMAT", "INPUT_ENVS_FORMAT"},
Value: envsFormat,
},
}
@@ -241,6 +247,7 @@ func run(c *cli.Context) error {
if s := c.String("script.string"); s != "" {
scripts = append(scripts, s)
}
plugin := Plugin{
Config: Config{
Key: c.String("ssh-key"),
@@ -256,6 +263,7 @@ func run(c *cli.Context) error {
Script: scripts,
ScriptStop: c.Bool("script.stop"),
Envs: c.StringSlice("envs"),
EnvsFormat: c.String("envs.format"),
Debug: c.Bool("debug"),
Sync: c.Bool("sync"),
Ciphers: c.StringSlice("ciphers"),
+64 -33
View File
@@ -17,6 +17,7 @@ var (
errMissingHost = errors.New("Error: missing server host")
errMissingPasswordOrKey = errors.New("Error: can't connect without a private SSH key or password")
errCommandTimeOut = errors.New("Error: command timeout")
envsFormat = "export {NAME}={VALUE}"
)
type (
@@ -40,6 +41,7 @@ type (
Sync bool
Ciphers []string
UseInsecureCipher bool
EnvsFormat string
}
// Plugin structure
@@ -65,6 +67,7 @@ func (p Plugin) hostPort(host string) (string, string) {
}
func (p Plugin) exec(host string, wg *sync.WaitGroup, errChannel chan error) {
defer wg.Done()
host, port := p.hostPort(host)
// Create MakeConfig instance with remote username, server address and path to private key.
ssh := &easyssh.MakeConfig{
@@ -102,7 +105,7 @@ func (p Plugin) exec(host string, wg *sync.WaitGroup, errChannel chan error) {
for _, key := range p.Config.Envs {
key = strings.ToUpper(key)
if val, found := os.LookupEnv(key); found {
env = append(env, "export "+key+"="+escapeArg(val))
env = append(env, p.format(p.Config.EnvsFormat, "{NAME}", key, "{VALUE}", escapeArg(val)))
}
}
@@ -117,38 +120,41 @@ func (p Plugin) exec(host string, wg *sync.WaitGroup, errChannel chan error) {
stdoutChan, stderrChan, doneChan, errChan, err := ssh.Stream(strings.Join(p.Config.Script, "\n"), p.Config.CommandTimeout)
if err != nil {
errChannel <- err
} else {
// read from the output channel until the done signal is passed
isTimeout := true
loop:
for {
select {
case isTimeout = <-doneChan:
break loop
case outline := <-stdoutChan:
if outline != "" {
p.log(host, "out:", outline)
}
case errline := <-stderrChan:
if errline != "" {
p.log(host, "err:", errline)
}
case err = <-errChan:
return
}
// read from the output channel until the done signal is passed
isTimeout := true
loop:
for {
select {
case isTimeout = <-doneChan:
break loop
case outline := <-stdoutChan:
if outline != "" {
p.log(host, "out:", outline)
}
}
// get exit code or command error.
if err != nil {
errChannel <- err
}
// command time out
if !isTimeout {
errChannel <- errCommandTimeOut
case errline := <-stderrChan:
if errline != "" {
p.log(host, "err:", errline)
}
case err = <-errChan:
}
}
wg.Done()
// get exit code or command error.
if err != nil {
errChannel <- err
}
// command time out
if !isTimeout {
errChannel <- errCommandTimeOut
}
}
func (p Plugin) format(format string, args ...string) string {
r := strings.NewReplacer(args...)
return r.Replace(format)
}
func (p Plugin) log(host string, message ...interface{}) {
@@ -164,6 +170,8 @@ func (p Plugin) log(host string, message ...interface{}) {
// Exec executes the plugin.
func (p Plugin) Exec() error {
p.Config.Host = trimValues(p.Config.Host)
if len(p.Config.Host) == 0 {
return errMissingHost
}
@@ -172,14 +180,22 @@ func (p Plugin) Exec() error {
return errMissingPasswordOrKey
}
if p.Config.EnvsFormat == "" {
p.Config.EnvsFormat = envsFormat
}
wg := sync.WaitGroup{}
wg.Add(len(p.Config.Host))
errChannel := make(chan error)
finished := make(chan struct{})
for _, host := range p.Config.Host {
if p.Config.Sync {
p.exec(host, &wg, errChannel)
} else {
if p.Config.Sync {
go func() {
for _, host := range p.Config.Host {
p.exec(host, &wg, errChannel)
}
}()
} else {
for _, host := range p.Config.Host {
go p.exec(host, &wg, errChannel)
}
}
@@ -230,3 +246,18 @@ func (p Plugin) scriptCommands() []string {
return commands
}
func trimValues(keys []string) []string {
var newKeys []string
for _, value := range keys {
value = strings.TrimSpace(value)
if len(value) == 0 {
continue
}
newKeys = append(newKeys, value)
}
return newKeys
}
+35
View File
@@ -440,6 +440,41 @@ func TestFingerprint(t *testing.T) {
assert.Equal(t, unindent(expected), unindent(buffer.String()))
}
func TestScriptStopWithMultipleHostAndSyncMode(t *testing.T) {
var (
buffer bytes.Buffer
expected = `
======CMD======
mkdir a/b/c
mkdir d/e/f
======END======
err: mkdir: can't create directory 'a/b/c': No such file or directory
`
)
plugin := Plugin{
Config: Config{
Host: []string{"", "localhost"},
Username: "drone-scp",
Port: 22,
KeyPath: "./tests/.ssh/id_rsa",
Script: []string{
"mkdir a/b/c",
"mkdir d/e/f",
},
CommandTimeout: 10 * time.Second,
ScriptStop: true,
Sync: true,
},
Writer: &buffer,
}
err := plugin.Exec()
assert.NotNil(t, err)
assert.Equal(t, unindent(expected), unindent(buffer.String()))
}
func TestScriptStop(t *testing.T) {
var (
buffer bytes.Buffer