Merge pull request #115 from fudanchii/ssh-deploy
Enable deployment via ssh
This commit is contained in:
commit
452eb232e9
5 changed files with 226 additions and 1 deletions
1
Makefile
1
Makefile
|
@ -49,6 +49,7 @@ test:
|
|||
go test -v github.com/drone/drone/pkg/database/testing
|
||||
go test -v github.com/drone/drone/pkg/mail
|
||||
go test -v github.com/drone/drone/pkg/model
|
||||
go test -v github.com/drone/drone/pkg/plugin/deploy
|
||||
go test -v github.com/drone/drone/pkg/queue
|
||||
|
||||
install:
|
||||
|
|
|
@ -125,7 +125,7 @@ func RepoCreateGithub(w http.ResponseWriter, r *http.Request, u *User) error {
|
|||
// create the github key, or update if one already exists
|
||||
_, err := client.RepoKeys.CreateUpdate(owner, name, repo.PublicKey, keyName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Unable to add Private Key to your GitHub repository")
|
||||
return fmt.Errorf("Unable to add Public Key to your GitHub repository")
|
||||
}
|
||||
} else {
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@ type Deploy struct {
|
|||
Heroku *Heroku `yaml:"heroku,omitempty"`
|
||||
Nodejitsu *Nodejitsu `yaml:"nodejitsu,omitempty"`
|
||||
Openshift *Openshift `yaml:"openshift,omitempty"`
|
||||
SSH *SSH `yaml:"ssh,omitempty"`
|
||||
}
|
||||
|
||||
func (d *Deploy) Write(f *buildfile.Buildfile) {
|
||||
|
@ -43,4 +44,7 @@ func (d *Deploy) Write(f *buildfile.Buildfile) {
|
|||
if d.Openshift != nil {
|
||||
d.Openshift.Write(f)
|
||||
}
|
||||
if d.SSH != nil {
|
||||
d.SSH.Write(f)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1 +1,96 @@
|
|||
package deploy
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/drone/drone/pkg/build/buildfile"
|
||||
)
|
||||
|
||||
// SSH struct holds configuration data for deployment
|
||||
// via ssh, deployment done by scp-ing file(s) listed
|
||||
// in artifacts to the target host, and then run cmd
|
||||
// remotely.
|
||||
// It is assumed that the target host already
|
||||
// add this repo public key in the host's `authorized_hosts`
|
||||
// file. And the private key is already copied to `.ssh/id_rsa`
|
||||
// inside the build container. No further check will be done.
|
||||
type SSH struct {
|
||||
|
||||
// Target is the deployment host in this format
|
||||
// user@hostname:/full/path <PORT>
|
||||
//
|
||||
// PORT may be omitted if its default to port 22.
|
||||
Target string `yaml:"target,omitempty"`
|
||||
|
||||
// Artifacts is a list of files/dirs to be deployed
|
||||
// to the target host. If artifacts list more than one file
|
||||
// it will be compressed into a single tar.gz file.
|
||||
// if artifacts contain:
|
||||
// - GITARCHIVE
|
||||
//
|
||||
// other file listed in artifacts will be ignored, instead, we will
|
||||
// create git archive from the current revision and deploy that file
|
||||
// alone.
|
||||
// If you need to deploy the git archive along with some other files,
|
||||
// please use build script to create the git archive, and then list
|
||||
// the archive name here with the other files.
|
||||
Artifacts []string `yaml:"artifacts,omitempty"`
|
||||
|
||||
// Cmd is a single command executed at target host after the artifacts
|
||||
// is deployed.
|
||||
Cmd string `yaml:"cmd,omitempty"`
|
||||
}
|
||||
|
||||
// Write down the buildfile
|
||||
func (s *SSH) Write(f *buildfile.Buildfile) {
|
||||
host := strings.SplitN(s.Target, " ", 2)
|
||||
if len(host) == 1 {
|
||||
host = append(host, "22")
|
||||
}
|
||||
if _, err := strconv.Atoi(host[1]); err != nil {
|
||||
host[1] = "22"
|
||||
}
|
||||
|
||||
// Is artifact created?
|
||||
artifact := false
|
||||
|
||||
for _, a := range s.Artifacts {
|
||||
if a == "GITARCHIVE" {
|
||||
artifact = createGitArchive(f)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if len(s.Artifacts) > 1 && !artifact {
|
||||
artifact = compress(f, s.Artifacts)
|
||||
} else if len(s.Artifacts) == 1 {
|
||||
f.WriteCmdSilent(fmt.Sprintf("ARTIFACT=%s", s.Artifacts[0]))
|
||||
artifact = true
|
||||
}
|
||||
|
||||
if artifact {
|
||||
scpCmd := "scp -o StrictHostKeyChecking=no -P %s ${ARTIFACT} %s"
|
||||
f.WriteCmd(fmt.Sprintf(scpCmd, host[1], host[0]))
|
||||
}
|
||||
|
||||
if len(s.Cmd) > 0 {
|
||||
sshCmd := "ssh -o StrictHostKeyChecking=no -p %s %s %s"
|
||||
f.WriteCmd(fmt.Sprintf(sshCmd, host[1], strings.SplitN(host[0], ":", 2)[0], s.Cmd))
|
||||
}
|
||||
}
|
||||
|
||||
func createGitArchive(f *buildfile.Buildfile) bool {
|
||||
f.WriteCmdSilent("COMMIT=$(git rev-parse HEAD)")
|
||||
f.WriteCmdSilent("ARTIFACT=${PWD##*/}-${COMMIT}.tar.gz")
|
||||
f.WriteCmdSilent("git archive --format=tar.gz --prefix=${PWD##*/}/ ${COMMIT} > ${ARTIFACT}")
|
||||
return true
|
||||
}
|
||||
|
||||
func compress(f *buildfile.Buildfile, files []string) bool {
|
||||
cmd := "tar -cf ${ARTIFACT} %s"
|
||||
f.WriteCmdSilent("ARTIFACT=${PWD##*/}.tar.gz")
|
||||
f.WriteCmdSilent(fmt.Sprintf(cmd, strings.Join(files, " ")))
|
||||
return true
|
||||
}
|
||||
|
|
125
pkg/plugin/deploy/ssh_test.go
Normal file
125
pkg/plugin/deploy/ssh_test.go
Normal file
|
@ -0,0 +1,125 @@
|
|||
package deploy
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/drone/drone/pkg/build/buildfile"
|
||||
|
||||
"launchpad.net/goyaml"
|
||||
)
|
||||
|
||||
// emulate Build struct
|
||||
type build struct {
|
||||
Deploy *Deploy `yaml:"deploy,omitempty"`
|
||||
}
|
||||
|
||||
var sampleYml = `
|
||||
deploy:
|
||||
ssh:
|
||||
target: user@test.example.com
|
||||
cmd: /opt/bin/redeploy.sh
|
||||
`
|
||||
|
||||
var sampleYml1 = `
|
||||
deploy:
|
||||
ssh:
|
||||
target: user@test.example.com:/srv/app/location 2212
|
||||
artifacts:
|
||||
- build.result
|
||||
cmd: /opt/bin/redeploy.sh
|
||||
`
|
||||
|
||||
var sampleYml2 = `
|
||||
deploy:
|
||||
ssh:
|
||||
target: user@test.example.com:/srv/app/location 2212
|
||||
artifacts:
|
||||
- build.result
|
||||
- config/file
|
||||
cmd: /opt/bin/redeploy.sh
|
||||
`
|
||||
|
||||
var sampleYml3 = `
|
||||
deploy:
|
||||
ssh:
|
||||
target: user@test.example.com:/srv/app/location 2212
|
||||
artifacts:
|
||||
- GITARCHIVE
|
||||
cmd: /opt/bin/redeploy.sh
|
||||
`
|
||||
|
||||
func setUp(input string) (string, error) {
|
||||
var buildStruct build
|
||||
err := goyaml.Unmarshal([]byte(input), &buildStruct)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
bf := buildfile.New()
|
||||
buildStruct.Deploy.Write(bf)
|
||||
return bf.String(), err
|
||||
}
|
||||
|
||||
func TestSSHNoArtifact(t *testing.T) {
|
||||
bscr, err := setUp(sampleYml)
|
||||
if err != nil {
|
||||
t.Fatalf("Can't unmarshal deploy script: %s", err)
|
||||
}
|
||||
|
||||
if strings.Contains(bscr, `scp`) {
|
||||
t.Error("Expect script not to contains scp command")
|
||||
}
|
||||
|
||||
if !strings.Contains(bscr, "ssh -o StrictHostKeyChecking=no -p 22 user@test.example.com /opt/bin/redeploy.sh") {
|
||||
t.Error("Expect script to contains ssh command")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSSHOneArtifact(t *testing.T) {
|
||||
bscr, err := setUp(sampleYml1)
|
||||
if err != nil {
|
||||
t.Fatalf("Can't unmarshal deploy script: %s", err)
|
||||
}
|
||||
|
||||
if !strings.Contains(bscr, "ARTIFACT=build.result") {
|
||||
t.Errorf("Expect script to contains artifact")
|
||||
}
|
||||
|
||||
if !strings.Contains(bscr, "scp -o StrictHostKeyChecking=no -P 2212 ${ARTIFACT} user@test.example.com:/srv/app/location") {
|
||||
t.Errorf("Expect script to contains scp command, got:\n%s", bscr)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSSHMultiArtifact(t *testing.T) {
|
||||
bscr, err := setUp(sampleYml2)
|
||||
if err != nil {
|
||||
t.Fatalf("Can't unmarshal deploy script: %s", err)
|
||||
}
|
||||
|
||||
if !strings.Contains(bscr, "ARTIFACT=${PWD##*/}.tar.gz") {
|
||||
t.Errorf("Expect script to contains artifact")
|
||||
}
|
||||
|
||||
if !strings.Contains(bscr, "tar -cf ${ARTIFACT} build.result config/file") {
|
||||
t.Errorf("Expect script to contains tar command. got:\n", bscr)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSSHGitArchive(t *testing.T) {
|
||||
bscr, err := setUp(sampleYml3)
|
||||
if err != nil {
|
||||
t.Fatalf("Can't unmarshal deploy script: %s", err)
|
||||
}
|
||||
|
||||
if !strings.Contains(bscr, "COMMIT=$(git rev-parse HEAD)") {
|
||||
t.Errorf("Expect script to contains commit ref")
|
||||
}
|
||||
|
||||
if !strings.Contains(bscr, "ARTIFACT=${PWD##*/}-${COMMIT}.tar.gz") {
|
||||
t.Errorf("Expect script to contains artifact")
|
||||
}
|
||||
|
||||
if !strings.Contains(bscr, "git archive --format=tar.gz --prefix=${PWD##*/}/ ${COMMIT} > ${ARTIFACT}") {
|
||||
t.Errorf("Expect script to run git archive")
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue