mirror of
https://github.com/appleboy/drone-ssh.git
synced 2026-02-17 09:48:50 +08:00
feat: Add time out flag. (#53)
* feat: Add time out flag. * fix testing Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>
This commit is contained in:
21
vendor/github.com/appleboy/easyssh-proxy/LICENSE
generated
vendored
Normal file
21
vendor/github.com/appleboy/easyssh-proxy/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2017 Bo-Yi Wu
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
60
vendor/github.com/appleboy/easyssh-proxy/Makefile
generated
vendored
Normal file
60
vendor/github.com/appleboy/easyssh-proxy/Makefile
generated
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
.PHONY: test drone-ssh build fmt vet errcheck lint install update release-dirs release-build release-copy release-check release coverage
|
||||
|
||||
PACKAGES ?= $(shell go list ./... | grep -v /vendor/)
|
||||
|
||||
all: build
|
||||
|
||||
fmt:
|
||||
find . -name "*.go" -type f -not -path "./vendor/*" | xargs gofmt -s -w
|
||||
|
||||
vet:
|
||||
go vet $(PACKAGES)
|
||||
|
||||
errcheck:
|
||||
@hash errcheck > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||
go get -u github.com/kisielk/errcheck; \
|
||||
fi
|
||||
errcheck $(PACKAGES)
|
||||
|
||||
lint:
|
||||
@hash golint > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||
go get -u github.com/golang/lint/golint; \
|
||||
fi
|
||||
for PKG in $(PACKAGES); do golint -set_exit_status $$PKG || exit 1; done;
|
||||
|
||||
unconvert:
|
||||
@hash unconvert > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||
go get -u github.com/mdempsky/unconvert; \
|
||||
fi
|
||||
for PKG in $(PACKAGES); do unconvert -v $$PKG || exit 1; done;
|
||||
|
||||
test:
|
||||
for PKG in $(PACKAGES); do go test -v -cover -coverprofile $$GOPATH/src/$$PKG/coverage.txt $$PKG || exit 1; done;
|
||||
|
||||
html:
|
||||
go tool cover -html=coverage.txt
|
||||
|
||||
coverage:
|
||||
sed -i '/main.go/d' .cover/coverage.txt
|
||||
curl -s https://codecov.io/bash > .codecov && \
|
||||
chmod +x .codecov && \
|
||||
./.codecov -f .cover/coverage.txt
|
||||
|
||||
clean:
|
||||
go clean -x -i ./...
|
||||
rm -rf coverage.txt $(EXECUTABLE) $(DIST) vendor
|
||||
|
||||
ssh-server:
|
||||
adduser -h /home/drone-scp -s /bin/bash -D -S drone-scp
|
||||
echo drone-scp:1234 | chpasswd
|
||||
mkdir -p /home/drone-scp/.ssh
|
||||
chmod 700 /home/drone-scp/.ssh
|
||||
cp tests/.ssh/id_rsa.pub /home/drone-scp/.ssh/authorized_keys
|
||||
chown -R drone-scp /home/drone-scp/.ssh
|
||||
# install ssh and start server
|
||||
apk add --update openssh openrc
|
||||
rm -rf /etc/ssh/ssh_host_rsa_key /etc/ssh/ssh_host_dsa_key
|
||||
./tests/entrypoint.sh /usr/sbin/sshd -D &
|
||||
|
||||
version:
|
||||
@echo $(VERSION)
|
||||
5
vendor/github.com/appleboy/easyssh-proxy/README.md
generated
vendored
Normal file
5
vendor/github.com/appleboy/easyssh-proxy/README.md
generated
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
# easyssh-proxy
|
||||
|
||||
[](https://godoc.org/github.com/appleboy/easyssh-proxy) [](http://drone.wu-boy.com/appleboy/easyssh-proxy) [](https://codecov.io/gh/appleboy/easyssh-proxy) [](https://goreportcard.com/report/github.com/appleboy/easyssh-proxy)
|
||||
|
||||
easyssh-proxy provides a simple implementation of some SSH protocol features in Go
|
||||
250
vendor/github.com/appleboy/easyssh-proxy/easyssh.go
generated
vendored
Normal file
250
vendor/github.com/appleboy/easyssh-proxy/easyssh.go
generated
vendored
Normal file
@@ -0,0 +1,250 @@
|
||||
// Package easyssh provides a simple implementation of some SSH protocol
|
||||
// features in Go. You can simply run a command on a remote server or get a file
|
||||
// even simpler than native console SSH client. You don't need to think about
|
||||
// Dials, sessions, defers, or public keys... Let easyssh think about it!
|
||||
package easyssh
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"golang.org/x/crypto/ssh"
|
||||
"golang.org/x/crypto/ssh/agent"
|
||||
)
|
||||
|
||||
// MakeConfig Contains main authority information.
|
||||
// User field should be a name of user on remote server (ex. john in ssh john@example.com).
|
||||
// Server field should be a remote machine address (ex. example.com in ssh john@example.com)
|
||||
// Key is a path to private key on your local machine.
|
||||
// Port is SSH server port on remote machine.
|
||||
// Note: easyssh looking for private key in user's home directory (ex. /home/john + Key).
|
||||
// Then ensure your Key begins from '/' (ex. /.ssh/id_rsa)
|
||||
type MakeConfig struct {
|
||||
User string
|
||||
Server string
|
||||
Key string
|
||||
KeyPath string
|
||||
Port string
|
||||
Password string
|
||||
Timeout time.Duration
|
||||
}
|
||||
|
||||
type sshConfig struct {
|
||||
User string
|
||||
Key string
|
||||
KeyPath string
|
||||
Password string
|
||||
Timeout time.Duration
|
||||
}
|
||||
|
||||
// returns ssh.Signer from user you running app home path + cutted key path.
|
||||
// (ex. pubkey,err := getKeyFile("/.ssh/id_rsa") )
|
||||
func getKeyFile(keypath string) (ssh.Signer, error) {
|
||||
buf, err := ioutil.ReadFile(keypath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pubkey, err := ssh.ParsePrivateKey(buf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return pubkey, nil
|
||||
}
|
||||
|
||||
func getSSHConfig(config sshConfig) *ssh.ClientConfig {
|
||||
// auths holds the detected ssh auth methods
|
||||
auths := []ssh.AuthMethod{}
|
||||
|
||||
// figure out what auths are requested, what is supported
|
||||
if config.Password != "" {
|
||||
auths = append(auths, ssh.Password(config.Password))
|
||||
}
|
||||
|
||||
if sshAgent, err := net.Dial("unix", os.Getenv("SSH_AUTH_SOCK")); err == nil {
|
||||
auths = append(auths, ssh.PublicKeysCallback(agent.NewClient(sshAgent).Signers))
|
||||
defer sshAgent.Close()
|
||||
}
|
||||
|
||||
if config.KeyPath != "" {
|
||||
if pubkey, err := getKeyFile(config.KeyPath); err == nil {
|
||||
auths = append(auths, ssh.PublicKeys(pubkey))
|
||||
}
|
||||
}
|
||||
|
||||
if config.Key != "" {
|
||||
signer, _ := ssh.ParsePrivateKey([]byte(config.Key))
|
||||
auths = append(auths, ssh.PublicKeys(signer))
|
||||
}
|
||||
|
||||
return &ssh.ClientConfig{
|
||||
Timeout: config.Timeout,
|
||||
User: config.User,
|
||||
Auth: auths,
|
||||
}
|
||||
}
|
||||
|
||||
// connect to remote server using MakeConfig struct and returns *ssh.Session
|
||||
func (ssh_conf *MakeConfig) connect() (*ssh.Session, error) {
|
||||
config := getSSHConfig(sshConfig{
|
||||
User: ssh_conf.User,
|
||||
Key: ssh_conf.Key,
|
||||
KeyPath: ssh_conf.KeyPath,
|
||||
Password: ssh_conf.Password,
|
||||
Timeout: ssh_conf.Timeout,
|
||||
})
|
||||
|
||||
client, err := ssh.Dial("tcp", net.JoinHostPort(ssh_conf.Server, ssh_conf.Port), config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
session, err := client.NewSession()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return session, nil
|
||||
}
|
||||
|
||||
// Stream returns one channel that combines the stdout and stderr of the command
|
||||
// as it is run on the remote machine, and another that sends true when the
|
||||
// command is done. The sessions and channels will then be closed.
|
||||
func (ssh_conf *MakeConfig) Stream(command string, timeout int) (stdout chan string, stderr chan string, done chan bool, err error) {
|
||||
// connect to remote host
|
||||
session, err := ssh_conf.connect()
|
||||
if err != nil {
|
||||
return stdout, stderr, done, err
|
||||
}
|
||||
// connect to both outputs (they are of type io.Reader)
|
||||
outReader, err := session.StdoutPipe()
|
||||
if err != nil {
|
||||
return stdout, stderr, done, err
|
||||
}
|
||||
errReader, err := session.StderrPipe()
|
||||
if err != nil {
|
||||
return stdout, stderr, done, err
|
||||
}
|
||||
// combine outputs, create a line-by-line scanner
|
||||
stdoutReader := io.MultiReader(outReader)
|
||||
stderrReader := io.MultiReader(errReader)
|
||||
err = session.Start(command)
|
||||
stdoutScanner := bufio.NewScanner(stdoutReader)
|
||||
stderrScanner := bufio.NewScanner(stderrReader)
|
||||
// continuously send the command's output over the channel
|
||||
stdoutChan := make(chan string)
|
||||
stderrChan := make(chan string)
|
||||
done = make(chan bool)
|
||||
|
||||
go func(stdoutScanner, stderrScanner *bufio.Scanner, stdoutChan, stderrChan chan string, done chan bool) {
|
||||
defer close(stdoutChan)
|
||||
defer close(stderrChan)
|
||||
defer close(done)
|
||||
|
||||
timeoutChan := time.After(time.Duration(timeout) * time.Second)
|
||||
res := make(chan bool, 1)
|
||||
|
||||
go func() {
|
||||
for stdoutScanner.Scan() {
|
||||
stdoutChan <- stdoutScanner.Text()
|
||||
}
|
||||
for stderrScanner.Scan() {
|
||||
stderrChan <- stderrScanner.Text()
|
||||
}
|
||||
// close all of our open resources
|
||||
res <- true
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-res:
|
||||
stdoutChan <- ""
|
||||
stderrChan <- ""
|
||||
done <- true
|
||||
case <-timeoutChan:
|
||||
stdoutChan <- ""
|
||||
stderrChan <- "Run Command Timeout!"
|
||||
done <- false
|
||||
}
|
||||
|
||||
session.Close()
|
||||
}(stdoutScanner, stderrScanner, stdoutChan, stderrChan, done)
|
||||
return stdoutChan, stderrChan, done, err
|
||||
}
|
||||
|
||||
// Run command on remote machine and returns its stdout as a string
|
||||
func (ssh_conf *MakeConfig) Run(command string, timeout int) (outStr string, errStr string, isTimeout bool, err error) {
|
||||
stdoutChan, stderrChan, doneChan, err := ssh_conf.Stream(command, timeout)
|
||||
if err != nil {
|
||||
return outStr, errStr, isTimeout, err
|
||||
}
|
||||
// read from the output channel until the done signal is passed
|
||||
stillGoing := true
|
||||
for stillGoing {
|
||||
select {
|
||||
case isTimeout = <-doneChan:
|
||||
stillGoing = false
|
||||
case outline := <-stdoutChan:
|
||||
if outline != "" {
|
||||
outStr += outline + "\n"
|
||||
}
|
||||
case errline := <-stderrChan:
|
||||
if errline != "" {
|
||||
errStr += errline + "\n"
|
||||
}
|
||||
}
|
||||
}
|
||||
// return the concatenation of all signals from the output channel
|
||||
return outStr, errStr, isTimeout, err
|
||||
}
|
||||
|
||||
// Scp uploads sourceFile to remote machine like native scp console app.
|
||||
func (ssh_conf *MakeConfig) Scp(sourceFile string, etargetFile string) error {
|
||||
session, err := ssh_conf.connect()
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer session.Close()
|
||||
|
||||
targetFile := filepath.Base(etargetFile)
|
||||
|
||||
src, srcErr := os.Open(sourceFile)
|
||||
|
||||
if srcErr != nil {
|
||||
return srcErr
|
||||
}
|
||||
|
||||
srcStat, statErr := src.Stat()
|
||||
|
||||
if statErr != nil {
|
||||
return statErr
|
||||
}
|
||||
|
||||
go func() {
|
||||
w, _ := session.StdinPipe()
|
||||
|
||||
fmt.Fprintln(w, "C0644", srcStat.Size(), targetFile)
|
||||
|
||||
if srcStat.Size() > 0 {
|
||||
io.Copy(w, src)
|
||||
fmt.Fprint(w, "\x00")
|
||||
w.Close()
|
||||
} else {
|
||||
fmt.Fprint(w, "\x00")
|
||||
w.Close()
|
||||
}
|
||||
}()
|
||||
|
||||
if err := session.Run(fmt.Sprintf("scp -tr %s", etargetFile)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user