mirror of
https://github.com/appleboy/drone-ssh.git
synced 2025-05-09 18:23:21 +08:00
Merge pull request #1 from drone-plugins/feature/unification
Multiple minor improvements, more unification
This commit is contained in:
commit
af49113a20
14
.drone.yml
14
.drone.yml
@ -1,14 +1,10 @@
|
||||
build:
|
||||
image: golang:1.5
|
||||
environment:
|
||||
- GO15VENDOREXPERIMENT=1
|
||||
- GOOS=linux
|
||||
- GOARCH=amd64
|
||||
- CGO_ENABLED=0
|
||||
commands:
|
||||
- go get
|
||||
- go build
|
||||
- go test
|
||||
- make deps
|
||||
- make vet
|
||||
- make build
|
||||
- make test
|
||||
|
||||
publish:
|
||||
docker:
|
||||
@ -21,7 +17,7 @@ publish:
|
||||
|
||||
plugin:
|
||||
name: SSH
|
||||
desc: Use SSH to execute commands on a remote host
|
||||
desc: Execute commands on a remote host through SSH
|
||||
type: deploy
|
||||
image: plugins/drone-ssh
|
||||
labels:
|
||||
|
14
Dockerfile
14
Dockerfile
@ -1,8 +1,14 @@
|
||||
# Docker image for the Drone build runner
|
||||
# Docker image for the Drone Swift plugin
|
||||
#
|
||||
# CGO_ENABLED=0 go build -a -tags netgo
|
||||
# docker build --rm=true -t plugins/drone-ssh .
|
||||
# cd $GOPATH/src/github.com/drone-plugins/drone-ssh
|
||||
# make deps build docker
|
||||
|
||||
FROM alpine:3.3
|
||||
|
||||
RUN apk update && \
|
||||
apk add \
|
||||
ca-certificates && \
|
||||
rm -rf /var/cache/apk/*
|
||||
|
||||
FROM gliderlabs/alpine:3.1
|
||||
ADD drone-ssh /bin/
|
||||
ENTRYPOINT ["/bin/drone-ssh"]
|
31
Makefile
Normal file
31
Makefile
Normal file
@ -0,0 +1,31 @@
|
||||
.PHONY: clean deps test build docker
|
||||
|
||||
export GOOS ?= linux
|
||||
export GOARCH ?= amd64
|
||||
export CGO_ENABLED ?= 0
|
||||
|
||||
CI_BUILD_NUMBER ?= 0
|
||||
|
||||
LDFLAGS += -X "main.buildDate=$(shell date -u '+%Y-%m-%d %H:%M:%S %Z')"
|
||||
LDFLAGS += -X "main.build=$(CI_BUILD_NUMBER)"
|
||||
|
||||
clean:
|
||||
go clean -i ./...
|
||||
|
||||
deps:
|
||||
go get -t ./...
|
||||
|
||||
test:
|
||||
go test -cover ./...
|
||||
|
||||
fmt:
|
||||
go fmt ./...
|
||||
|
||||
vet:
|
||||
go vet ./...
|
||||
|
||||
build:
|
||||
go build -ldflags '-s -w $(LDFLAGS)'
|
||||
|
||||
docker:
|
||||
docker build --rm=true -t plugins/drone-ssh .
|
82
README.md
82
README.md
@ -1,2 +1,82 @@
|
||||
# drone-ssh
|
||||
Drone plugin for executing remote ssh commands
|
||||
|
||||
[](http://beta.drone.io/drone-plugins/drone-ssh)
|
||||
[](https://imagelayers.io/?images=plugins/drone-ssh:latest 'Get your own badge on imagelayers.io')
|
||||
|
||||
Drone plugin for executing commands on a remote host through SSH
|
||||
|
||||
## Usage
|
||||
|
||||
```
|
||||
./drone-ssh <<EOF
|
||||
{
|
||||
"repo" : {
|
||||
"owner": "foo",
|
||||
"name": "bar",
|
||||
"full_name": "foo/bar"
|
||||
},
|
||||
"build" : {
|
||||
"number": 22,
|
||||
"status": "success",
|
||||
"started_at": 1421029603,
|
||||
"finished_at": 1421029813,
|
||||
"commit": "9f2849d5",
|
||||
"branch": "master",
|
||||
"message": "Update the Readme",
|
||||
"author": "johnsmith",
|
||||
"author_email": "john.smith@gmail.com"
|
||||
},
|
||||
"vargs": {
|
||||
"host": "foo.com",
|
||||
"user": "root",
|
||||
"port": 22,
|
||||
"commands": [
|
||||
"echo hello",
|
||||
"echo world"
|
||||
]
|
||||
}
|
||||
}
|
||||
EOF
|
||||
```
|
||||
|
||||
## Docker
|
||||
|
||||
Build the Docker container using `make`:
|
||||
|
||||
```
|
||||
make deps build docker
|
||||
```
|
||||
|
||||
### Example
|
||||
|
||||
```sh
|
||||
docker run -i plugins/drone-ssh <<EOF
|
||||
{
|
||||
"repo" : {
|
||||
"owner": "foo",
|
||||
"name": "bar",
|
||||
"full_name": "foo/bar"
|
||||
},
|
||||
"build" : {
|
||||
"number": 22,
|
||||
"status": "success",
|
||||
"started_at": 1421029603,
|
||||
"finished_at": 1421029813,
|
||||
"commit": "9f2849d5",
|
||||
"branch": "master",
|
||||
"message": "Update the Readme",
|
||||
"author": "johnsmith",
|
||||
"author_email": "john.smith@gmail.com"
|
||||
},
|
||||
"vargs": {
|
||||
"host": "foo.com",
|
||||
"user": "root",
|
||||
"port": 22,
|
||||
"commands": [
|
||||
"echo hello",
|
||||
"echo world"
|
||||
]
|
||||
}
|
||||
}
|
||||
EOF
|
||||
```
|
||||
|
64
main.go
64
main.go
@ -8,83 +8,85 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/drone/drone-plugin-go/plugin"
|
||||
"github.com/drone/drone-go/drone"
|
||||
"github.com/drone/drone-go/plugin"
|
||||
"golang.org/x/crypto/ssh"
|
||||
)
|
||||
|
||||
// Params stores the git clone parameters used to
|
||||
// configure and customzie the git clone behavior.
|
||||
type Params struct {
|
||||
Commands []string `json:"commands"`
|
||||
Login string `json:"user"`
|
||||
Port int `json:"port"`
|
||||
Host StrSlice `json:"host"`
|
||||
Sleep int `json:"sleep"`
|
||||
}
|
||||
var (
|
||||
build string
|
||||
buildDate string
|
||||
)
|
||||
|
||||
func main() {
|
||||
v := new(Params)
|
||||
w := new(plugin.Workspace)
|
||||
plugin.Param("workspace", w)
|
||||
plugin.Param("vargs", &v)
|
||||
fmt.Printf("Drone SSH Plugin built at %s\n", buildDate)
|
||||
|
||||
workspace := drone.Workspace{}
|
||||
vargs := Params{}
|
||||
|
||||
plugin.Param("workspace", &workspace)
|
||||
plugin.Param("vargs", &vargs)
|
||||
plugin.MustParse()
|
||||
|
||||
for i, host := range v.Host.Slice() {
|
||||
err := run(w.Keys, v, host)
|
||||
for i, host := range vargs.Host.Slice() {
|
||||
err := run(workspace.Keys, &vargs, host)
|
||||
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
if v.Sleep != 0 && i != v.Host.Len()-1 {
|
||||
fmt.Printf("$ sleep %d\n", v.Sleep)
|
||||
time.Sleep(time.Duration(v.Sleep) * time.Second)
|
||||
|
||||
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(keys *plugin.Keypair, params *Params, host string) error {
|
||||
|
||||
// if no username is provided assume root
|
||||
if len(params.Login) == 0 {
|
||||
func run(key *drone.Key, params *Params, host string) error {
|
||||
if params.Login == "" {
|
||||
params.Login = "root"
|
||||
}
|
||||
|
||||
// if no port is provided use default
|
||||
if params.Port == 0 {
|
||||
params.Port = 22
|
||||
}
|
||||
|
||||
// join the host and port if necessary
|
||||
addr := net.JoinHostPort(
|
||||
host,
|
||||
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(key.Private))
|
||||
|
||||
signer, err := ssh.ParsePrivateKey([]byte(keys.Private))
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error parsing private key. %s.", err)
|
||||
return fmt.Errorf("Error: Failed to parse private key. %s", err)
|
||||
}
|
||||
|
||||
config := &ssh.ClientConfig{
|
||||
User: params.Login,
|
||||
Auth: []ssh.AuthMethod{ssh.PublicKeys(signer)},
|
||||
Auth: []ssh.AuthMethod{
|
||||
ssh.PublicKeys(signer),
|
||||
},
|
||||
}
|
||||
|
||||
client, err := ssh.Dial("tcp", addr, config)
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error dialing server. %s.", err)
|
||||
return fmt.Errorf("Error: Failed to dial to server. %s", err)
|
||||
}
|
||||
|
||||
session, err := client.NewSession()
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error starting ssh session. %s.", err)
|
||||
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"))
|
||||
}
|
||||
|
14
main_test.go
14
main_test.go
@ -5,7 +5,7 @@ import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/drone/drone-plugin-go/plugin"
|
||||
"github.com/drone/drone-go/drone"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -15,14 +15,13 @@ var (
|
||||
)
|
||||
|
||||
func TestRun(t *testing.T) {
|
||||
|
||||
// only runs the test if a host server is provided
|
||||
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)
|
||||
}
|
||||
@ -30,14 +29,19 @@ func TestRun(t *testing.T) {
|
||||
params := &Params{
|
||||
Commands: []string{"whoami", "time", "ps -ax"},
|
||||
Login: user,
|
||||
Host: StrSlice{[]string{host}},
|
||||
Host: drone.NewStringSlice(
|
||||
[]string{
|
||||
host,
|
||||
},
|
||||
),
|
||||
}
|
||||
|
||||
keys := &plugin.Keypair{
|
||||
keys := &drone.Key{
|
||||
Private: string(out),
|
||||
}
|
||||
|
||||
err = run(keys, params, host)
|
||||
|
||||
if err != nil {
|
||||
t.Errorf("Unable to run SSH commands. %s.", err)
|
||||
}
|
||||
|
50
types.go
50
types.go
@ -1,45 +1,13 @@
|
||||
package main
|
||||
|
||||
import "encoding/json"
|
||||
import (
|
||||
"github.com/drone/drone-go/drone"
|
||||
)
|
||||
|
||||
// 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
|
||||
type Params struct {
|
||||
Commands []string `json:"commands"`
|
||||
Login string `json:"user"`
|
||||
Port int `json:"port"`
|
||||
Host drone.StringSlice `json:"host"`
|
||||
Sleep int `json:"sleep"`
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user