mirror of
https://github.com/appleboy/drone-ssh.git
synced 2025-05-09 18:23:21 +08:00
ability to run commands on multiple hosts
This commit is contained in:
parent
2821783224
commit
a1294ae508
21
DOCS.md
21
DOCS.md
@ -5,7 +5,7 @@ Use the SSH plugin to execute commands on a remote server. The following paramet
|
|||||||
* `user` - user to log in as on the remote machine
|
* `user` - user to log in as on the remote machine
|
||||||
* `commands` - list of commands to execute
|
* `commands` - list of commands to execute
|
||||||
|
|
||||||
The following is a sample SSH configuration in your .drone.yml file:
|
Example configuration in your .drone.yml file:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
deploy:
|
deploy:
|
||||||
@ -18,4 +18,23 @@ deploy:
|
|||||||
- echo world
|
- echo world
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Example multi-host configuration in your .drone.yml file:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
deploy:
|
||||||
|
ssh:
|
||||||
|
host:
|
||||||
|
- foo.com
|
||||||
|
- bar.com
|
||||||
|
user: root
|
||||||
|
port: 22
|
||||||
|
commands:
|
||||||
|
- echo hello
|
||||||
|
- echo world
|
||||||
|
```
|
||||||
|
|
||||||
|
In the above example Drone executes the commands on multiple hosts sequentially. If the commands fail on a single host this plugin exits immediatly, and will not run your commands on the remaining hosts in the list.
|
||||||
|
|
||||||
|
## Keys
|
||||||
|
|
||||||
The plugin authenticates to your server using a per-repository SSH key generated by Drone. You can find the public key in your repository settings in Drone. You will need to copy / paste this key into your `~/.ssh/authorized_keys` file on your remote machine.
|
The plugin authenticates to your server using a per-repository SSH key generated by Drone. You can find the public key in your repository settings in Drone. You will need to copy / paste this key into your `~/.ssh/authorized_keys` file on your remote machine.
|
17
main.go
17
main.go
@ -17,7 +17,7 @@ type Params struct {
|
|||||||
Commands []string `json:"commands"`
|
Commands []string `json:"commands"`
|
||||||
Login string `json:"user"`
|
Login string `json:"user"`
|
||||||
Port int `json:"port"`
|
Port int `json:"port"`
|
||||||
Host string `json:"host"`
|
Host StrSlice `json:"host"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
@ -27,13 +27,15 @@ func main() {
|
|||||||
plugin.Param("vargs", &v)
|
plugin.Param("vargs", &v)
|
||||||
plugin.MustParse()
|
plugin.MustParse()
|
||||||
|
|
||||||
err := run(w.Keys, v)
|
for _, host := range v.Host.Slice() {
|
||||||
|
err := run(w.Keys, v, host)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func run(keys *plugin.Keypair, params *Params) error {
|
func run(keys *plugin.Keypair, params *Params, host string) error {
|
||||||
|
|
||||||
// if no username is provided assume root
|
// if no username is provided assume root
|
||||||
if len(params.Login) == 0 {
|
if len(params.Login) == 0 {
|
||||||
@ -46,11 +48,14 @@ func run(keys *plugin.Keypair, params *Params) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// join the host and port if necessary
|
// join the host and port if necessary
|
||||||
host := net.JoinHostPort(
|
addr := net.JoinHostPort(
|
||||||
params.Host,
|
host,
|
||||||
strconv.Itoa(params.Port),
|
strconv.Itoa(params.Port),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// trace command used for debugging in the build logs
|
||||||
|
fmt.Printf("$ ssh %s@%s -p %d\n", params.Login, addr, params.Port)
|
||||||
|
|
||||||
signer, err := ssh.ParsePrivateKey([]byte(keys.Private))
|
signer, err := ssh.ParsePrivateKey([]byte(keys.Private))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Error parsing private key. %s.", err)
|
return fmt.Errorf("Error parsing private key. %s.", err)
|
||||||
@ -61,7 +66,7 @@ func run(keys *plugin.Keypair, params *Params) error {
|
|||||||
Auth: []ssh.AuthMethod{ssh.PublicKeys(signer)},
|
Auth: []ssh.AuthMethod{ssh.PublicKeys(signer)},
|
||||||
}
|
}
|
||||||
|
|
||||||
client, err := ssh.Dial("tcp", host, config)
|
client, err := ssh.Dial("tcp", addr, config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Error dialing server. %s.", err)
|
return fmt.Errorf("Error dialing server. %s.", err)
|
||||||
}
|
}
|
||||||
|
45
types.go
Normal file
45
types.go
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import "encoding/json"
|
||||||
|
|
||||||
|
// StrSlice representes a string or an array of strings.
|
||||||
|
// We need to override the json decoder to accept both options.
|
||||||
|
type StrSlice struct {
|
||||||
|
parts []string
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON decodes the byte slice whether it's a string or an array of strings.
|
||||||
|
// This method is needed to implement json.Unmarshaler.
|
||||||
|
func (e *StrSlice) UnmarshalJSON(b []byte) error {
|
||||||
|
if len(b) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
p := make([]string, 0, 1)
|
||||||
|
if err := json.Unmarshal(b, &p); err != nil {
|
||||||
|
var s string
|
||||||
|
if err := json.Unmarshal(b, &s); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
p = append(p, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
e.parts = p
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Len returns the number of parts of the StrSlice.
|
||||||
|
func (e *StrSlice) Len() int {
|
||||||
|
if e == nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return len(e.parts)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Slice gets the parts of the StrSlice as a Slice of string.
|
||||||
|
func (e *StrSlice) Slice() []string {
|
||||||
|
if e == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return e.parts
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user