mirror of
https://github.com/appleboy/drone-ssh.git
synced 2025-05-09 18:23:21 +08:00
Migrated plugin to 0.5 structure
This commit is contained in:
parent
69b686b375
commit
c816c75fc6
@ -1 +0,0 @@
|
||||
eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkExMjhHQ00ifQ.zIZPFzihqcXgWK0D3eLbrSXYK_1PQznfeVzKjVBsEs9C1nHAm71GjtQ4WlFI17pgs3JMZYMCJGg5jylg3WMD2Fxqeomucv2dTpeg4R-qacuUerkBk_3fyThNh2IOqzNbr3hHPyyVwtelXk6eInJQhoa6HBaXfpPTW0_x5JHNGeMuqWIXpZceLENm46t9zh-DsqnrXnhay4gB6kjoYAM8fxV4bRYbj-ef8XTg4G6ZS7dlTZdrOWIvFmYWNGF4dnRFN7EtxvkT7zTutAeNa4wZsc-p3qX03LKsc3kNijUhLtywh1AzZ-wEiOa2nGLQov5SLEnmpQbwJZQ9meS---LG8Q.TaGSxaCL3H-MXwSp.E0OSE8VqYg1Bs8Qxo2uIi03HMQ5Cxg4THetXBK5Z5G2_Qxt_7LMo1eOmvpi_aHOEPrd53hz3Uzn0C4PTZiD6TfMG_viMiJVHpzBayN3ZjUAs7JRavheCXgnlO5u1kgyp3XmRW089JbBFl2fMs8a7Bob8jLoQPJFyEIBGEJuUayE3pmSQ98Hhw3u1KAxbLG9iJqWErICNEZoSWPfhuWU-K5FMtxP0Ewx0ceqHKhJTQdqZycsFS7aeOgq5MtUMF3T7nsFX-tnrdv4siCiYT4kI9nGTQCCW3i3-nLq9ZkD5JbLs_IG1sfVNabd1hI2OpjAfL717Mp37AFbd0dPhlU7kxUytrTwiU3JVd1vr6EA1gd5TGA3CHOxjeVLpfHXMW6SJKTE3a0Wgi_YsWz336JkMkQkywgv8g6A0mpUhMcEt_Is.sSdenvAYiKXLuRdPszf25A
|
49
.drone.yml
49
.drone.yml
@ -1,39 +1,42 @@
|
||||
build:
|
||||
image: golang:1.5
|
||||
environment:
|
||||
- CGO_ENABLED=0
|
||||
commands:
|
||||
- make deps
|
||||
- make vet
|
||||
- make build
|
||||
- make test
|
||||
workspace:
|
||||
base: /go
|
||||
path: src/github.com/drone-plugins/drone-ssh
|
||||
|
||||
publish:
|
||||
pipeline:
|
||||
test:
|
||||
image: golang:1.6
|
||||
environment:
|
||||
- CGO_ENABLED=0
|
||||
commands:
|
||||
- go vet
|
||||
- go test -cover -coverprofile=coverage.out
|
||||
- go build -ldflags "-s -w -X main.build=$DRONE_BUILD_NUMBER"
|
||||
coverage:
|
||||
when:
|
||||
branch: master
|
||||
docker:
|
||||
username: $$DOCKER_USER
|
||||
password: $$DOCKER_PASS
|
||||
email: $$DOCKER_EMAIL
|
||||
repo: plugins/drone-ssh
|
||||
tag: latest
|
||||
event: push
|
||||
latest:
|
||||
image: docker
|
||||
storage_driver: overlay
|
||||
repo: plugins/ssh
|
||||
tags: [ "latest", "1.0", "1" ]
|
||||
when:
|
||||
branch: master
|
||||
docker:
|
||||
username: $$DOCKER_USER
|
||||
password: $$DOCKER_PASS
|
||||
email: $$DOCKER_EMAIL
|
||||
repo: plugins/drone-ssh
|
||||
tag: develop
|
||||
event: push
|
||||
develop:
|
||||
image: docker
|
||||
storage_driver: overlay
|
||||
repo: plugins/ssh
|
||||
tags: [ "develop" ]
|
||||
when:
|
||||
branch: develop
|
||||
event: push
|
||||
|
||||
plugin:
|
||||
name: SSH
|
||||
desc: Execute commands on a remote host through SSH
|
||||
type: deploy
|
||||
image: plugins/drone-ssh
|
||||
image: plugins/ssh
|
||||
labels:
|
||||
- deploy
|
||||
- ssh
|
||||
|
1
.drone.yml.sig
Normal file
1
.drone.yml.sig
Normal file
@ -0,0 +1 @@
|
||||
eyJhbGciOiJIUzI1NiJ9.d29ya3NwYWNlOgogIGJhc2U6IC9nbwogIHBhdGg6IHNyYy9naXRodWIuY29tL2Ryb25lLXBsdWdpbnMvZHJvbmUtc3NoCgpwaXBlbGluZToKICB0ZXN0OgogICAgaW1hZ2U6IGdvbGFuZzoxLjYKICAgIGVudmlyb25tZW50OgogICAgICAtIENHT19FTkFCTEVEPTAKICAgIGNvbW1hbmRzOgogICAgICAtIGdvIHZldAogICAgICAtIGdvIHRlc3QgLWNvdmVyIC1jb3ZlcnByb2ZpbGU9Y292ZXJhZ2Uub3V0CiAgICAgIC0gZ28gYnVpbGQgLWxkZmxhZ3MgIi1zIC13IC1YIG1haW4uYnVpbGQ9JERST05FX0JVSUxEX05VTUJFUiIKICBjb3ZlcmFnZToKICAgIHdoZW46CiAgICAgIGJyYW5jaDogbWFzdGVyCiAgICAgIGV2ZW50OiBwdXNoCiAgbGF0ZXN0OgogICAgaW1hZ2U6IGRvY2tlcgogICAgc3RvcmFnZV9kcml2ZXI6IG92ZXJsYXkKICAgIHJlcG86IHBsdWdpbnMvc3NoCiAgICB0YWdzOiBbICJsYXRlc3QiLCAiMS4wIiwgIjEiIF0KICAgIHdoZW46CiAgICAgIGJyYW5jaDogbWFzdGVyCiAgICAgIGV2ZW50OiBwdXNoCiAgZGV2ZWxvcDoKICAgIGltYWdlOiBkb2NrZXIKICAgIHN0b3JhZ2VfZHJpdmVyOiBvdmVybGF5CiAgICByZXBvOiBwbHVnaW5zL3NzaAogICAgdGFnczogWyAiZGV2ZWxvcCIgXQogICAgd2hlbjoKICAgICAgYnJhbmNoOiBkZXZlbG9wCiAgICAgIGV2ZW50OiBwdXNoCgpwbHVnaW46CiAgbmFtZTogU1NICiAgZGVzYzogRXhlY3V0ZSBjb21tYW5kcyBvbiBhIHJlbW90ZSBob3N0IHRocm91Z2ggU1NICiAgdHlwZTogZGVwbG95CiAgaW1hZ2U6IHBsdWdpbnMvc3NoCiAgbGFiZWxzOgogICAgLSBkZXBsb3kKICAgIC0gc3NoCg.k_ZUu8xFHIBhnh3ysdNRuerUITgmD-BwVgyYMfhmtmw
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -22,6 +22,7 @@ _testmain.go
|
||||
*.exe
|
||||
*.test
|
||||
*.prof
|
||||
.env
|
||||
|
||||
coverage.out
|
||||
drone-ssh
|
||||
|
64
DOCS.md
64
DOCS.md
@ -1,14 +1,48 @@
|
||||
Use the SSH plugin to execute commands on a remote server. The following parameters are used to configure this plugin:
|
||||
Use the SSH plugin to execute commands on a remote server. You will need to
|
||||
supply Drone with a private SSH key to being able to connect to a host.
|
||||
|
||||
* `host` - address or IP of the remote machine
|
||||
* `port` - port to connect to on the remote machine
|
||||
* `user` - user to log in as on the remote machine
|
||||
* `commands` - list of commands to execute
|
||||
## Overview
|
||||
|
||||
Example configuration in your .drone.yml file:
|
||||
The following parameters are used to configure the plugin:
|
||||
|
||||
* **host** - address or IP of the remote machine
|
||||
* **port** - port to connect to on the remote machine
|
||||
* **user** - user to log in as on the remote machine
|
||||
* **key** - private SSH key for the remote machine
|
||||
* **sleep** - sleep for seconds between host connections
|
||||
* **commands** - list of commands to execute
|
||||
|
||||
The following secret values can be set to configure the plugin.
|
||||
|
||||
* **SSH_HOST** - corresponds to **host**
|
||||
* **SSH_PORT** - corresponds to **port**
|
||||
* **SSH_USER** - corresponds to **user**
|
||||
* **SSH_KEY** - corresponds to **key**
|
||||
* **SSH_SLEEP** - corresponds to **sleep**
|
||||
|
||||
It is highly recommended to put the **SSH_KEY** into a secret so it is not
|
||||
exposed to users. This can be done using the drone-cli.
|
||||
|
||||
```bash
|
||||
drone secret add --image=ssh \
|
||||
octocat/hello-world SSH_KEY @${HOME}/.ssh/id_rsa
|
||||
```
|
||||
|
||||
Then sign the YAML file after all secrets are added.
|
||||
|
||||
```bash
|
||||
drone sign octocat/hello-world
|
||||
```
|
||||
|
||||
See [secrets](http://readme.drone.io/0.5/usage/secrets/) for additional
|
||||
information on secrets
|
||||
|
||||
## Examples
|
||||
|
||||
Example configuration in your .drone.yml file for a single host:
|
||||
|
||||
```yaml
|
||||
deploy:
|
||||
pipeline:
|
||||
ssh:
|
||||
host: foo.com
|
||||
user: root
|
||||
@ -18,10 +52,10 @@ deploy:
|
||||
- echo world
|
||||
```
|
||||
|
||||
Example multi-host configuration in your .drone.yml file:
|
||||
Example configuration in your .drone.yml file for multiple hosts:
|
||||
|
||||
```yaml
|
||||
deploy:
|
||||
pipeline:
|
||||
ssh:
|
||||
host:
|
||||
- foo.com
|
||||
@ -34,10 +68,10 @@ deploy:
|
||||
- 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.
|
||||
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.
|
||||
|
||||
The above example also uses the `sleep` parameter. The sleep parameter instructs Drone to sleep for N seconds between host executions.
|
||||
|
||||
## 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 above example also uses the `sleep` parameter. The sleep parameter
|
||||
instructs Drone to sleep for N seconds between host executions.
|
||||
|
10
Dockerfile
10
Dockerfile
@ -1,13 +1,9 @@
|
||||
# Docker image for the Drone Swift plugin
|
||||
#
|
||||
# cd $GOPATH/src/github.com/drone-plugins/drone-ssh
|
||||
# make deps build docker
|
||||
|
||||
FROM alpine:3.3
|
||||
FROM alpine:3.4
|
||||
|
||||
RUN apk update && \
|
||||
apk add \
|
||||
ca-certificates && \
|
||||
ca-certificates \
|
||||
openssh-client && \
|
||||
rm -rf /var/cache/apk/*
|
||||
|
||||
ADD drone-ssh /bin/
|
||||
|
34
Makefile
34
Makefile
@ -1,34 +0,0 @@
|
||||
.PHONY: all clean deps fmt vet test docker
|
||||
|
||||
EXECUTABLE ?= drone-ssh
|
||||
IMAGE ?= plugins/$(EXECUTABLE)
|
||||
COMMIT ?= $(shell git rev-parse --short HEAD)
|
||||
|
||||
LDFLAGS = -X "main.buildCommit=$(COMMIT)"
|
||||
PACKAGES = $(shell go list ./... | grep -v /vendor/)
|
||||
|
||||
all: deps build test
|
||||
|
||||
clean:
|
||||
go clean -i ./...
|
||||
|
||||
deps:
|
||||
go get -t ./...
|
||||
|
||||
fmt:
|
||||
go fmt $(PACKAGES)
|
||||
|
||||
vet:
|
||||
go vet $(PACKAGES)
|
||||
|
||||
test:
|
||||
@for PKG in $(PACKAGES); do go test -cover -coverprofile $$GOPATH/src/$$PKG/coverage.out $$PKG || exit 1; done;
|
||||
|
||||
docker:
|
||||
GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -ldflags '-s -w $(LDFLAGS)'
|
||||
docker build --rm -t $(IMAGE) .
|
||||
|
||||
$(EXECUTABLE): $(wildcard *.go)
|
||||
go build -ldflags '-s -w $(LDFLAGS)'
|
||||
|
||||
build: $(EXECUTABLE)
|
115
README.md
115
README.md
@ -6,106 +6,43 @@
|
||||
|
||||
Drone plugin to execute commands on a remote host through SSH. For the usage information and a listing of the available options please take a look at [the docs](DOCS.md).
|
||||
|
||||
## Binary
|
||||
## Build
|
||||
|
||||
Build the binary using `make`:
|
||||
Build the binary with the following commands:
|
||||
|
||||
```
|
||||
make deps build
|
||||
```
|
||||
|
||||
### Example
|
||||
|
||||
```sh
|
||||
./drone-ssh <<EOF
|
||||
{
|
||||
"repo": {
|
||||
"clone_url": "git://github.com/drone/drone",
|
||||
"owner": "drone",
|
||||
"name": "drone",
|
||||
"full_name": "drone/drone"
|
||||
},
|
||||
"system": {
|
||||
"link_url": "https://beta.drone.io"
|
||||
},
|
||||
"build": {
|
||||
"number": 22,
|
||||
"status": "success",
|
||||
"started_at": 1421029603,
|
||||
"finished_at": 1421029813,
|
||||
"message": "Update the Readme",
|
||||
"author": "johnsmith",
|
||||
"author_email": "john.smith@gmail.com"
|
||||
"event": "push",
|
||||
"branch": "master",
|
||||
"commit": "436b7a6e2abaddfd35740527353e78a227ddcb2c",
|
||||
"ref": "refs/heads/master"
|
||||
},
|
||||
"workspace": {
|
||||
"root": "/drone/src",
|
||||
"path": "/drone/src/github.com/drone/drone"
|
||||
},
|
||||
"vargs": {
|
||||
"host": "foo.com",
|
||||
"user": "root",
|
||||
"port": 22,
|
||||
"commands": [
|
||||
"echo hello",
|
||||
"echo world"
|
||||
]
|
||||
}
|
||||
}
|
||||
EOF
|
||||
export GO15VENDOREXPERIMENT=1
|
||||
go build
|
||||
go test
|
||||
```
|
||||
|
||||
## Docker
|
||||
|
||||
Build the container using `make`:
|
||||
Build the docker image with the following commands:
|
||||
|
||||
```
|
||||
make deps docker
|
||||
export GO15VENDOREXPERIMENT=1
|
||||
GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -a -tags netgo
|
||||
```
|
||||
|
||||
### Example
|
||||
Please note incorrectly building the image for the correct x64 linux and with GCO disabled will result in an error when running the Docker image:
|
||||
|
||||
```
|
||||
docker: Error response from daemon: Container command
|
||||
'/bin/drone-ssh' not found or does not exist..
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
Execute a single remote command
|
||||
|
||||
```sh
|
||||
docker run -i plugins/drone-ssh <<EOF
|
||||
{
|
||||
"repo": {
|
||||
"clone_url": "git://github.com/drone/drone",
|
||||
"owner": "drone",
|
||||
"name": "drone",
|
||||
"full_name": "drone/drone"
|
||||
},
|
||||
"system": {
|
||||
"link_url": "https://beta.drone.io"
|
||||
},
|
||||
"build": {
|
||||
"number": 22,
|
||||
"status": "success",
|
||||
"started_at": 1421029603,
|
||||
"finished_at": 1421029813,
|
||||
"message": "Update the Readme",
|
||||
"author": "johnsmith",
|
||||
"author_email": "john.smith@gmail.com"
|
||||
"event": "push",
|
||||
"branch": "master",
|
||||
"commit": "436b7a6e2abaddfd35740527353e78a227ddcb2c",
|
||||
"ref": "refs/heads/master"
|
||||
},
|
||||
"workspace": {
|
||||
"root": "/drone/src",
|
||||
"path": "/drone/src/github.com/drone/drone"
|
||||
},
|
||||
"vargs": {
|
||||
"host": "foo.com",
|
||||
"user": "root",
|
||||
"port": 22,
|
||||
"commands": [
|
||||
"echo hello",
|
||||
"echo world"
|
||||
]
|
||||
}
|
||||
}
|
||||
EOF
|
||||
docker run --rm \
|
||||
-e PLUGIN_HOST=foo.com \
|
||||
-e PLUGIN_USER=root \
|
||||
-e PLUGIN_KEY="$(cat ${HOME}/.ssh/id_rsa)" \
|
||||
-e PLUGIN_COMMANDS=whoami \
|
||||
-v $(pwd)/$(pwd) \
|
||||
-w $(pwd) \
|
||||
plugins/ssh
|
||||
```
|
||||
|
143
main.go
143
main.go
@ -1,91 +1,80 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"log"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/drone/drone-go/drone"
|
||||
"github.com/drone/drone-go/plugin"
|
||||
"golang.org/x/crypto/ssh"
|
||||
"github.com/joho/godotenv"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
var (
|
||||
buildCommit string
|
||||
)
|
||||
var version string // build number set at compile-time
|
||||
|
||||
func main() {
|
||||
fmt.Printf("Drone SSH Plugin built from %s\n", buildCommit)
|
||||
|
||||
workspace := drone.Workspace{}
|
||||
vargs := Params{}
|
||||
|
||||
plugin.Param("workspace", &workspace)
|
||||
plugin.Param("vargs", &vargs)
|
||||
plugin.MustParse()
|
||||
|
||||
for i, host := range vargs.Host.Slice() {
|
||||
err := run(workspace.Keys, &vargs, host)
|
||||
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if vargs.Sleep != 0 && i != vargs.Host.Len()-1 {
|
||||
fmt.Printf("$ sleep %d\n", vargs.Sleep)
|
||||
time.Sleep(time.Duration(vargs.Sleep) * time.Second)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func run(key *drone.Key, params *Params, host string) error {
|
||||
if params.Login == "" {
|
||||
params.Login = "root"
|
||||
}
|
||||
|
||||
if params.Port == 0 {
|
||||
params.Port = 22
|
||||
}
|
||||
|
||||
addr := net.JoinHostPort(
|
||||
host,
|
||||
strconv.Itoa(params.Port),
|
||||
)
|
||||
|
||||
fmt.Printf("$ ssh %s@%s -p %d\n", params.Login, addr, params.Port)
|
||||
signer, err := ssh.ParsePrivateKey([]byte(key.Private))
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error: Failed to parse private key. %s", err)
|
||||
}
|
||||
|
||||
config := &ssh.ClientConfig{
|
||||
User: params.Login,
|
||||
Auth: []ssh.AuthMethod{
|
||||
ssh.PublicKeys(signer),
|
||||
app := cli.NewApp()
|
||||
app.Name = "ssh plugin"
|
||||
app.Usage = "ssh plugin"
|
||||
app.Action = run
|
||||
app.Version = version
|
||||
app.Flags = []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "ssh-key",
|
||||
Usage: "private ssh key",
|
||||
EnvVar: "PLUGIN_SSH_KEY,SSH_KEY",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "user",
|
||||
Usage: "connect as user",
|
||||
EnvVar: "PLUGIN_USER,SSH_USER",
|
||||
Value: "root",
|
||||
},
|
||||
cli.StringSliceFlag{
|
||||
Name: "host",
|
||||
Usage: "connect to host",
|
||||
EnvVar: "PLUGIN_HOST,SSH_HOST",
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "port",
|
||||
Usage: "connect to port",
|
||||
EnvVar: "PLUGIN_PORT,SSH_PORT",
|
||||
Value: 22,
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "sleep",
|
||||
Usage: "sleep between hosts",
|
||||
EnvVar: "PLUGIN_SLEEP,SSH_SLEEP",
|
||||
},
|
||||
cli.StringSliceFlag{
|
||||
Name: "commands",
|
||||
Usage: "execute commands",
|
||||
EnvVar: "PLUGIN_COMMANDS",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "env-file",
|
||||
Usage: "source env file",
|
||||
},
|
||||
}
|
||||
|
||||
client, err := ssh.Dial("tcp", addr, config)
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error: Failed to dial to server. %s", err)
|
||||
if err := app.Run(os.Args); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
session, err := client.NewSession()
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error: Failed to start a SSH session. %s", err)
|
||||
}
|
||||
|
||||
defer session.Close()
|
||||
|
||||
session.Stdout = os.Stdout
|
||||
session.Stderr = os.Stderr
|
||||
|
||||
return session.Run(strings.Join(params.Commands, "\n"))
|
||||
}
|
||||
|
||||
func run(c *cli.Context) error {
|
||||
if c.String("env-file") != "" {
|
||||
_ = godotenv.Load(c.String("env-file"))
|
||||
}
|
||||
|
||||
plugin := Plugin{
|
||||
Config: Config{
|
||||
Key: c.String("ssh-key"),
|
||||
User: c.String("user"),
|
||||
Host: c.StringSlice("host"),
|
||||
Port: c.Int("port"),
|
||||
Sleep: c.Int("sleep"),
|
||||
Commands: c.StringSlice("commands"),
|
||||
},
|
||||
}
|
||||
|
||||
return plugin.Exec()
|
||||
}
|
||||
|
48
main_test.go
48
main_test.go
@ -1,48 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/drone/drone-go/drone"
|
||||
)
|
||||
|
||||
var (
|
||||
host = os.Getenv("TEST_SSH_HOST")
|
||||
user = os.Getenv("TEST_SSH_USER")
|
||||
key = os.Getenv("TEST_SSH_KEY")
|
||||
)
|
||||
|
||||
func TestRun(t *testing.T) {
|
||||
if len(host) == 0 {
|
||||
t.Skipf("TEST_SSH_HOST not provided")
|
||||
return
|
||||
}
|
||||
|
||||
out, err := ioutil.ReadFile(key)
|
||||
|
||||
if err != nil {
|
||||
t.Errorf("Unable to read or find a test privte key. %s", err)
|
||||
}
|
||||
|
||||
params := &Params{
|
||||
Commands: []string{"whoami", "time", "ps -ax"},
|
||||
Login: user,
|
||||
Host: drone.NewStringSlice(
|
||||
[]string{
|
||||
host,
|
||||
},
|
||||
),
|
||||
}
|
||||
|
||||
keys := &drone.Key{
|
||||
Private: string(out),
|
||||
}
|
||||
|
||||
err = run(keys, params, host)
|
||||
|
||||
if err != nil {
|
||||
t.Errorf("Unable to run SSH commands. %s.", err)
|
||||
}
|
||||
}
|
82
plugin.go
Normal file
82
plugin.go
Normal file
@ -0,0 +1,82 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"golang.org/x/crypto/ssh"
|
||||
)
|
||||
|
||||
type (
|
||||
Config struct {
|
||||
Key string `json:"key"`
|
||||
User string `json:"user"`
|
||||
Host []string `json:"host"`
|
||||
Port int `json:"port"`
|
||||
Sleep int `json:"sleep"`
|
||||
Commands []string `json:"commands"`
|
||||
}
|
||||
|
||||
Plugin struct {
|
||||
Config Config
|
||||
}
|
||||
)
|
||||
|
||||
func (p Plugin) Exec() error {
|
||||
if p.Config.Key == "" {
|
||||
return fmt.Errorf("Error: Can't connect without a private SSH key.")
|
||||
}
|
||||
|
||||
for i, host := range p.Config.Host {
|
||||
addr := net.JoinHostPort(
|
||||
host,
|
||||
strconv.Itoa(p.Config.Port),
|
||||
)
|
||||
|
||||
signer, err := ssh.ParsePrivateKey([]byte(p.Config.Key))
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error: Failed to parse private key. %s", err)
|
||||
}
|
||||
|
||||
config := &ssh.ClientConfig{
|
||||
User: p.Config.User,
|
||||
Auth: []ssh.AuthMethod{
|
||||
ssh.PublicKeys(signer),
|
||||
},
|
||||
}
|
||||
|
||||
fmt.Printf("+ ssh %s@%s -p %d\n", p.Config.User, addr, p.Config.Port)
|
||||
client, err := ssh.Dial("tcp", addr, config)
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error: Failed to dial to server. %s", err)
|
||||
}
|
||||
|
||||
session, err := client.NewSession()
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error: Failed to start a SSH session. %s", err)
|
||||
}
|
||||
|
||||
defer session.Close()
|
||||
|
||||
session.Stdout = os.Stdout
|
||||
session.Stderr = os.Stderr
|
||||
|
||||
if err := session.Run(strings.Join(p.Config.Commands, "\n")); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if p.Config.Sleep != 0 && i != len(p.Config.Host)-1 {
|
||||
fmt.Printf("+ sleep %d\n", p.Config.Sleep)
|
||||
time.Sleep(time.Duration(p.Config.Sleep) * time.Second)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
Loading…
Reference in New Issue
Block a user