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
|
|
45
.drone.yml
45
.drone.yml
@ -1,39 +1,42 @@
|
|||||||
build:
|
workspace:
|
||||||
image: golang:1.5
|
base: /go
|
||||||
|
path: src/github.com/drone-plugins/drone-ssh
|
||||||
|
|
||||||
|
pipeline:
|
||||||
|
test:
|
||||||
|
image: golang:1.6
|
||||||
environment:
|
environment:
|
||||||
- CGO_ENABLED=0
|
- CGO_ENABLED=0
|
||||||
commands:
|
commands:
|
||||||
- make deps
|
- go vet
|
||||||
- make vet
|
- go test -cover -coverprofile=coverage.out
|
||||||
- make build
|
- go build -ldflags "-s -w -X main.build=$DRONE_BUILD_NUMBER"
|
||||||
- make test
|
|
||||||
|
|
||||||
publish:
|
|
||||||
coverage:
|
coverage:
|
||||||
when:
|
when:
|
||||||
branch: master
|
branch: master
|
||||||
docker:
|
event: push
|
||||||
username: $$DOCKER_USER
|
latest:
|
||||||
password: $$DOCKER_PASS
|
image: docker
|
||||||
email: $$DOCKER_EMAIL
|
storage_driver: overlay
|
||||||
repo: plugins/drone-ssh
|
repo: plugins/ssh
|
||||||
tag: latest
|
tags: [ "latest", "1.0", "1" ]
|
||||||
when:
|
when:
|
||||||
branch: master
|
branch: master
|
||||||
docker:
|
event: push
|
||||||
username: $$DOCKER_USER
|
develop:
|
||||||
password: $$DOCKER_PASS
|
image: docker
|
||||||
email: $$DOCKER_EMAIL
|
storage_driver: overlay
|
||||||
repo: plugins/drone-ssh
|
repo: plugins/ssh
|
||||||
tag: develop
|
tags: [ "develop" ]
|
||||||
when:
|
when:
|
||||||
branch: develop
|
branch: develop
|
||||||
|
event: push
|
||||||
|
|
||||||
plugin:
|
plugin:
|
||||||
name: SSH
|
name: SSH
|
||||||
desc: Execute commands on a remote host through SSH
|
desc: Execute commands on a remote host through SSH
|
||||||
type: deploy
|
type: deploy
|
||||||
image: plugins/drone-ssh
|
image: plugins/ssh
|
||||||
labels:
|
labels:
|
||||||
- deploy
|
- deploy
|
||||||
- ssh
|
- 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
|
*.exe
|
||||||
*.test
|
*.test
|
||||||
*.prof
|
*.prof
|
||||||
|
.env
|
||||||
|
|
||||||
coverage.out
|
coverage.out
|
||||||
drone-ssh
|
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
|
## Overview
|
||||||
* `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
|
|
||||||
|
|
||||||
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
|
```yaml
|
||||||
deploy:
|
pipeline:
|
||||||
ssh:
|
ssh:
|
||||||
host: foo.com
|
host: foo.com
|
||||||
user: root
|
user: root
|
||||||
@ -18,10 +52,10 @@ deploy:
|
|||||||
- echo world
|
- echo world
|
||||||
```
|
```
|
||||||
|
|
||||||
Example multi-host configuration in your .drone.yml file:
|
Example configuration in your .drone.yml file for multiple hosts:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
deploy:
|
pipeline:
|
||||||
ssh:
|
ssh:
|
||||||
host:
|
host:
|
||||||
- foo.com
|
- foo.com
|
||||||
@ -34,10 +68,10 @@ deploy:
|
|||||||
- echo world
|
- 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.
|
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.
|
|
||||||
|
10
Dockerfile
10
Dockerfile
@ -1,13 +1,9 @@
|
|||||||
# Docker image for the Drone Swift plugin
|
FROM alpine:3.4
|
||||||
#
|
|
||||||
# cd $GOPATH/src/github.com/drone-plugins/drone-ssh
|
|
||||||
# make deps build docker
|
|
||||||
|
|
||||||
FROM alpine:3.3
|
|
||||||
|
|
||||||
RUN apk update && \
|
RUN apk update && \
|
||||||
apk add \
|
apk add \
|
||||||
ca-certificates && \
|
ca-certificates \
|
||||||
|
openssh-client && \
|
||||||
rm -rf /var/cache/apk/*
|
rm -rf /var/cache/apk/*
|
||||||
|
|
||||||
ADD drone-ssh /bin/
|
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).
|
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
|
export GO15VENDOREXPERIMENT=1
|
||||||
```
|
go build
|
||||||
|
go test
|
||||||
### 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
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Docker
|
## 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
|
```sh
|
||||||
docker run -i plugins/drone-ssh <<EOF
|
docker run --rm \
|
||||||
{
|
-e PLUGIN_HOST=foo.com \
|
||||||
"repo": {
|
-e PLUGIN_USER=root \
|
||||||
"clone_url": "git://github.com/drone/drone",
|
-e PLUGIN_KEY="$(cat ${HOME}/.ssh/id_rsa)" \
|
||||||
"owner": "drone",
|
-e PLUGIN_COMMANDS=whoami \
|
||||||
"name": "drone",
|
-v $(pwd)/$(pwd) \
|
||||||
"full_name": "drone/drone"
|
-w $(pwd) \
|
||||||
},
|
plugins/ssh
|
||||||
"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
|
|
||||||
```
|
```
|
||||||
|
143
main.go
143
main.go
@ -1,91 +1,80 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"log"
|
||||||
"net"
|
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/drone/drone-go/drone"
|
"github.com/joho/godotenv"
|
||||||
"github.com/drone/drone-go/plugin"
|
"github.com/urfave/cli"
|
||||||
"golang.org/x/crypto/ssh"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var version string // build number set at compile-time
|
||||||
buildCommit string
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
fmt.Printf("Drone SSH Plugin built from %s\n", buildCommit)
|
app := cli.NewApp()
|
||||||
|
app.Name = "ssh plugin"
|
||||||
workspace := drone.Workspace{}
|
app.Usage = "ssh plugin"
|
||||||
vargs := Params{}
|
app.Action = run
|
||||||
|
app.Version = version
|
||||||
plugin.Param("workspace", &workspace)
|
app.Flags = []cli.Flag{
|
||||||
plugin.Param("vargs", &vargs)
|
cli.StringFlag{
|
||||||
plugin.MustParse()
|
Name: "ssh-key",
|
||||||
|
Usage: "private ssh key",
|
||||||
for i, host := range vargs.Host.Slice() {
|
EnvVar: "PLUGIN_SSH_KEY,SSH_KEY",
|
||||||
err := run(workspace.Keys, &vargs, host)
|
},
|
||||||
|
cli.StringFlag{
|
||||||
if err != nil {
|
Name: "user",
|
||||||
fmt.Println(err)
|
Usage: "connect as user",
|
||||||
os.Exit(1)
|
EnvVar: "PLUGIN_USER,SSH_USER",
|
||||||
}
|
Value: "root",
|
||||||
|
},
|
||||||
if vargs.Sleep != 0 && i != vargs.Host.Len()-1 {
|
cli.StringSliceFlag{
|
||||||
fmt.Printf("$ sleep %d\n", vargs.Sleep)
|
Name: "host",
|
||||||
time.Sleep(time.Duration(vargs.Sleep) * time.Second)
|
Usage: "connect to host",
|
||||||
}
|
EnvVar: "PLUGIN_HOST,SSH_HOST",
|
||||||
}
|
},
|
||||||
}
|
cli.IntFlag{
|
||||||
|
Name: "port",
|
||||||
func run(key *drone.Key, params *Params, host string) error {
|
Usage: "connect to port",
|
||||||
if params.Login == "" {
|
EnvVar: "PLUGIN_PORT,SSH_PORT",
|
||||||
params.Login = "root"
|
Value: 22,
|
||||||
}
|
},
|
||||||
|
cli.IntFlag{
|
||||||
if params.Port == 0 {
|
Name: "sleep",
|
||||||
params.Port = 22
|
Usage: "sleep between hosts",
|
||||||
}
|
EnvVar: "PLUGIN_SLEEP,SSH_SLEEP",
|
||||||
|
},
|
||||||
addr := net.JoinHostPort(
|
cli.StringSliceFlag{
|
||||||
host,
|
Name: "commands",
|
||||||
strconv.Itoa(params.Port),
|
Usage: "execute commands",
|
||||||
)
|
EnvVar: "PLUGIN_COMMANDS",
|
||||||
|
},
|
||||||
fmt.Printf("$ ssh %s@%s -p %d\n", params.Login, addr, params.Port)
|
cli.StringFlag{
|
||||||
signer, err := ssh.ParsePrivateKey([]byte(key.Private))
|
Name: "env-file",
|
||||||
|
Usage: "source env file",
|
||||||
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),
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
client, err := ssh.Dial("tcp", addr, config)
|
if err := app.Run(os.Args); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("Error: Failed to dial to server. %s", err)
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
session, err := client.NewSession()
|
|
||||||
|
func run(c *cli.Context) error {
|
||||||
if err != nil {
|
if c.String("env-file") != "" {
|
||||||
return fmt.Errorf("Error: Failed to start a SSH session. %s", err)
|
_ = godotenv.Load(c.String("env-file"))
|
||||||
}
|
}
|
||||||
|
|
||||||
defer session.Close()
|
plugin := Plugin{
|
||||||
|
Config: Config{
|
||||||
session.Stdout = os.Stdout
|
Key: c.String("ssh-key"),
|
||||||
session.Stderr = os.Stderr
|
User: c.String("user"),
|
||||||
|
Host: c.StringSlice("host"),
|
||||||
return session.Run(strings.Join(params.Commands, "\n"))
|
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