使用crypto/ssh做的简单远程发布工具,scp,kill,start

  昨天项目组中的一个兄弟,问我我们远程发布是怎么做的,说现在他在维护好几个项目,每个项目需要部署的时候,要把编译好的文件拖过去,然后重启tomcat,本来很简单的事情,但是很浪费时间,问我有没有办法脚本化

  他自己的机器是windows的,scp命令可以通过安装解决,但是要远程关闭、开启tomcat,却不是脚本解决的事情,应该通过ssh解决,然后又需要放在其他机器上拿过去用,我就直接给提用golang实现了一个,代码如下:

 

package main

import (
	"bufio"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net"
	"os"
	"path/filepath"
	"strings"

	"golang.org/x/crypto/ssh"
)

var (
	auth       string
	publicKey  string
	passwd     string
	user       string
	ip_port    string
	dpath      string
	spath      string
	tomcatPath string
	killName   string
)

func main() {
	log.SetFlags(log.Lshortfile | log.Ltime | log.Ldate)
	flag.Parse()
	//read config file
	myConfig := new(Config)
	myConfig.InitConfig("./config.conf")
	auth = myConfig.Read("test", "auth")
	publicKey = myConfig.Read("test", "publicKey")
	passwd = myConfig.Read("test", "passwd")
	user = myConfig.Read("test", "user")
	ip_port = myConfig.Read("test", "ip_port")
	dpath = myConfig.Read("test", "dpath")
	spath = myConfig.Read("test", "spath")
	tomcatPath = myConfig.Read("test", "tomcatPath")
	killName = myConfig.Read("test", "killName")

	//目标文件
	//	File, err := os.Open(spath)
	//	if err != nil {
	//		fmt.Println("打开文件失败:", err)
	//		os.Exit(1)
	//	}
	//	info, _ := File.Stat()
	//	defer File.Close()

	var Client *ssh.Client
	var err error
	if strings.EqualFold(auth, "password") {
		Client, err = dail(user, passwd, ip_port)
	} else {
		Client, err = dailPublic(user, publicKey, ip_port)
	}
	if err != nil {
		fmt.Printf("连接%s失败.\n", err)
	}
	defer Client.Close()
	//	scp(Client, File, info.Size(), dpath)

	session, err := Client.NewSession()
	defer session.Close()
	if err != nil {
		fmt.Println("创建Session失败:", err)
		return
	}

	//遍历并复制
	err = filepath.Walk(spath, func(path string, f os.FileInfo, err error) error {
		if f == nil {
			return err
		}
		if f.IsDir() {
			newpath := strings.Replace(path, spath, dpath, len(spath))
			dname := strings.Replace(newpath, "\\", "/", -1)
			mkdir(Client, dname)
			return nil
		}
		//不是路径的,就复制文件
		File, err := os.Open(path)
		if err != nil {
			fmt.Println("打开文件失败:", err)
			os.Exit(1)
		}
		info, _ := File.Stat()
		defer File.Close()
		newpath := strings.Replace(path, spath, dpath, len(spath))
		dname := strings.Replace(newpath, "\\", "/", -1)
		fmt.Printf("dname %s\n", dname)
		scp(Client, File, info.Size(), dname)
		return nil
	})

	//停止tomcat
	kill(Client, killName)

	//启动tomcat
	start(Client, tomcatPath)
}

func scp(client *ssh.Client, File io.Reader, size int64, path string) {
	filename := filepath.Base(path)
	dirname := strings.Replace(filepath.Dir(path), "\\", "/", -1)
	session, err := client.NewSession()
	if err != nil {
		fmt.Println("创建Session失败:", err)
		return
	}
	go func() {
		w, _ := session.StdinPipe()
		fmt.Fprintln(w, "C0644", size, filename)
		io.CopyN(w, File, size)
		fmt.Fprint(w, "\x00")
		w.Close()
	}()
	fmt.Println("dir name is %s", dirname)
	if err := session.Run(fmt.Sprintf("/usr/bin/scp -qrt %s/", dirname)); err != nil {
		fmt.Println("执行scp命令失败:", err)
		if err != nil {
			session.Close()
			return
		}
	} else {
		fmt.Printf("%s 发送成功.\n")
		session.Close()
	}
	if session, err = client.NewSession(); err == nil {
		defer session.Close()
		buf, err := session.Output(fmt.Sprintf("/usr/bin/md5sum %s", path))
		if err != nil {
			fmt.Println("检查md5失败:", err)
			return
		}
		fmt.Printf("MD5:\n%s\n", string(buf))
	}
}

func mkdir(client *ssh.Client, path string) {
	fmt.Printf("create path %s\n", path)
	session, err := client.NewSession()
	if err != nil {
		fmt.Println("创建Session失败:", err)
		return
	}
	session.Run(fmt.Sprintf("[ ! -d %s ] && mkdir %s", path, path))
	defer session.Close()
}
func kill(client *ssh.Client, path string) {
	fmt.Printf("kill tomcat path is %s\n", path)
	session, err := client.NewSession()
	if err != nil {
		fmt.Println("创建Session失败:", err)
		return
	}
	session.Run(fmt.Sprintf("kill -9 `ps -ef|grep %s|grep -v 'grep'|awk '{print $2}'`", path))
	defer session.Close()
}
func start(client *ssh.Client, path string) {
	fmt.Printf("start tomcat path is %s\n", path)
	session, err := client.NewSession()
	if err != nil {
		fmt.Println("创建Session失败:", err)
		return
	}
	mt := fmt.Sprintf("source /etc/profile && %s/startup.sh", path)
	fmt.Println(mt)
	session.Stdout = os.Stdout
	session.Stderr = os.Stderr
	err = session.Run(mt)
	if err != nil {
		fmt.Println(err)
	}
	defer session.Close()
}

func dail(user, password, ip_port string) (*ssh.Client, error) {
	PassWd := []ssh.AuthMethod{ssh.Password(password)}
	Conf := ssh.ClientConfig{User: user, Auth: PassWd, HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error {
		return nil
	},
	}
	return ssh.Dial("tcp", ip_port, &Conf)
}
func dailPublic(user, publicKey, ip_port string) (*ssh.Client, error) {
	if strings.HasSuffix(publicKey, ".pub") {
		publicKey = strings.TrimSuffix(publicKey, ".pub")
	}
	signer, err := readPrivateKey(publicKey)
	if err != nil {
		fmt.Println(err)
	}
	PassWd := []ssh.AuthMethod{ssh.PublicKeys(signer)}
	Conf := ssh.ClientConfig{User: user, Auth: PassWd, HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error {
		return nil
	},
	}
	return ssh.Dial("tcp", ip_port, &Conf)
}

func readPrivateKey(path string) (ssh.Signer, error) {
	f, err := os.Open(path)
	if err != nil {
		return nil, err
	}
	defer f.Close()
	b, err := ioutil.ReadAll(f)
	if err != nil {
		return nil, err
	}
	return ssh.ParsePrivateKey(b)
}

//配置
type Config struct {
	Mymap  map[string]string
	strcet string
}

func (c *Config) InitConfig(path string) {
	c.Mymap = make(map[string]string)

	f, err := os.Open(path)
	if err != nil {
		panic(err)
	}
	defer f.Close()

	r := bufio.NewReader(f)
	for {
		b, _, err := r.ReadLine()
		if err != nil {
			if err == io.EOF {
				break
			}
			panic(err)
		}

		s := strings.TrimSpace(string(b))
		//fmt.Println(s)
		if strings.Index(s, "#") == 0 {
			continue
		}

		n1 := strings.Index(s, "[")
		n2 := strings.LastIndex(s, "]")
		if n1 > -1 && n2 > -1 && n2 > n1+1 {
			c.strcet = strings.TrimSpace(s[n1+1 : n2])
			continue
		}

		if len(c.strcet) == 0 {
			continue
		}
		index := strings.Index(s, "=")
		if index < 0 {
			continue
		}

		frist := strings.TrimSpace(s[:index])
		if len(frist) == 0 {
			continue
		}
		second := strings.TrimSpace(s[index+1:])

		pos := strings.Index(second, "\t#")
		if pos > -1 {
			second = second[0:pos]
		}

		pos = strings.Index(second, " #")
		if pos > -1 {
			second = second[0:pos]
		}

		pos = strings.Index(second, "\t//")
		if pos > -1 {
			second = second[0:pos]
		}

		pos = strings.Index(second, " //")
		if pos > -1 {
			second = second[0:pos]
		}

		if len(second) == 0 {
			continue
		}

		key := c.strcet + "." + frist
		c.Mymap[key] = strings.TrimSpace(second)
	}
}

func (c Config) Read(node, key string) string {
	key = node + "." + key
	v, found := c.Mymap[key]
	if !found {
		return ""
	}
	return v
}

  

使用配置文件指定参数:

[test]
auth = key                #password or key,现在只做了password和rsa公钥
publicKey = C:\Users\issuser\Desktop\key\id_rsa
passwd  = xxxx
user    = xxxx
ip_port = xx.xx.xx.xx:22
dpath   =  /work/ovuems/dapingAgent/webapps/dapingAgent/WEB-INF/classes/com    #目标路径
spath   =      D:\ideaSpace\dapingAgent\target\classes\com    #本地路径
killName = /work/ovuems/dapingAgent/bin                 #和下面的tomcat路径一致,防止需要杀掉多个进程的时候,用于灵活处理
tomcatPath = /work/ovuems/dapingAgent/bin      #tomcat路径,到bin这一级

 

说明:

该项目只做了4件事

1、连接ssh

2、复制本地编译好的class文件到远端(递归方式)

3、kill掉tomcat

4、启动tomcat

 

发现问题,启动tomcat的时候,启动不了,打印出来的消息为:

Neither the JAVA_HOME nor the JRE_HOME environment variable is defined At least one of these environment variable is needed to run this program

但是在机器上面查找,发现环境变量已经配置,没有办法,执行之前加上了 source /etc/profile予以解决

 

 

posted on 2018-08-21 15:04  轻歌曼舞  阅读(873)  评论(0编辑  收藏  举报