From 2fe7e43ba04782e34af0b2a0d346b2dede5a9d56 Mon Sep 17 00:00:00 2001 From: Thomas Boerger Date: Fri, 1 Jan 2016 21:32:30 +0100 Subject: [PATCH 1/7] Added makefile --- Makefile | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 Makefile diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..af971e3 --- /dev/null +++ b/Makefile @@ -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 . From b71dd3a715fda26809c6c4f05a209851141b31a4 Mon Sep 17 00:00:00 2001 From: Thomas Boerger Date: Fri, 1 Jan 2016 21:32:59 +0100 Subject: [PATCH 2/7] Use new make tasks within drone config --- .drone.yml | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/.drone.yml b/.drone.yml index 90f5079..7d547b5 100644 --- a/.drone.yml +++ b/.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: From 57898a9c71baae0d2a4cc65b2d6b7d6843fdba70 Mon Sep 17 00:00:00 2001 From: Thomas Boerger Date: Fri, 1 Jan 2016 21:33:20 +0100 Subject: [PATCH 3/7] Added empty line at the end of gitignore --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index a0a8502..fbd289b 100644 --- a/.gitignore +++ b/.gitignore @@ -23,4 +23,4 @@ _testmain.go *.test *.prof -drone-ssh \ No newline at end of file +drone-ssh From 01e226cea248be0e97ac743ab3e212760f96375a Mon Sep 17 00:00:00 2001 From: Thomas Boerger Date: Fri, 1 Jan 2016 21:34:17 +0100 Subject: [PATCH 4/7] Updated dockerfile to latest alpine image and updated docs --- Dockerfile | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/Dockerfile b/Dockerfile index 2e09c02..951bcdc 100644 --- a/Dockerfile +++ b/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"] \ No newline at end of file +ENTRYPOINT ["/bin/drone-ssh"] From a597624c7f2e78d1989a71385ab6bff100a3932a Mon Sep 17 00:00:00 2001 From: Thomas Boerger Date: Fri, 1 Jan 2016 21:34:37 +0100 Subject: [PATCH 5/7] Added useful content to readme --- README.md | 82 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 81 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 038158f..66735fc 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,82 @@ # drone-ssh -Drone plugin for executing remote ssh commands + +[![Build Status](http://beta.drone.io/api/badges/drone-plugins/drone-ssh/status.svg)](http://beta.drone.io/drone-plugins/drone-ssh) +[![](https://badge.imagelayers.io/plugins/drone-ssh:latest.svg)](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 < Date: Fri, 1 Jan 2016 21:35:10 +0100 Subject: [PATCH 6/7] Use the drone-go StringSlice instead of custom implementation --- types.go | 50 +++++++++----------------------------------------- 1 file changed, 9 insertions(+), 41 deletions(-) diff --git a/types.go b/types.go index 7831e0c..908f000 100644 --- a/types.go +++ b/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"` } From 4bfff6f1d676659c1d1867e0955cfe9a13d120df Mon Sep 17 00:00:00 2001 From: Thomas Boerger Date: Fri, 1 Jan 2016 21:36:08 +0100 Subject: [PATCH 7/7] Minor code refactoring --- main.go | 64 +++++++++++++++++++++++++++------------------------- main_test.go | 14 ++++++++---- 2 files changed, 42 insertions(+), 36 deletions(-) diff --git a/main.go b/main.go index 33205a9..30038cf 100644 --- a/main.go +++ b/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")) } diff --git a/main_test.go b/main_test.go index 31c781f..2f2fad0 100644 --- a/main_test.go +++ b/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) }