强化go get命令
-
利用ssh包,编写一个再window上类试运行go get的命令,将项目同时拉取到远程服务器的小项目
-
大概思路
- 读取配置文件,获取能够连接远程服务器的配置信息
- 创建远程服务器的session
- 解析命令,通过不同的信息,拉取不同的包
-
源代码
// conf.go
package main
import (
"log"
"os"
"path/filepath"
"gopkg.in/gcfg.v1"
valid "github.com/asaskevich/govalidator"
)
// Config 配置信息
type Config struct{
// Servers 多台服务器
Servers map[string]*Server
}
// Server 服务器
type Server struct{
User string
Port int
Host string
Password string
GoAbPath string // (利用which go 执行golang命令绝对路径获取)
}
var (
// Conf 全局配置变量
Conf = new(Config)
// HomePath 项目位置
HomePath string
)
func init(){
log.SetFlags(log.Ldate | log.Lshortfile)
var err error
HomePath,err = filepath.Abs(filepath.Dir(os.Args[0]))
if err!=nil{
log.Fatal(err)
}
}
// ReadConf 读取配置文件
func ReadConf(){
confPath := HomePath + "/conf.ini"
err := gcfg.ReadFileInto(Conf,confPath)
if err!=nil{
log.Fatal(err)
}
if Conf == nil {
log.Fatal("conf is nil")
}
for _,value := range Conf.Servers{
if !valid.IsIPv4(value.Host){
log.Fatal("请输入正确的IPv4类地址,错误的IP地址:",value.Host)
}
}
}
// main
package main
import (
"flag"
"fmt"
"io/ioutil"
"log"
"net"
"os"
// "os"
"os/exec"
"sync"
"time"
"golang.org/x/crypto/ssh"
)
// 设计思路
// 多台服务器同时go get 包,保证多台服务器上有go,并且能够翻墙
// SHost session and host
type SHost struct {
Session *ssh.Session
Host string
GoAbPath string
}
// flagGit 命令
var flagGit = flag.String("goget", "", "completely package go get,example goget github.com/gpmgo/gopm")
// sessions 对话组
var sHosts = make([]*SHost, 0)
// wg 线程组
var wg sync.WaitGroup
func init() {
// 读取配置文件
ReadConf()
// 创建对话组
for _, value := range Conf.Servers {
sHost, err := Connect(value)
if err != nil {
log.Println(err)
return
}
sHosts = append(sHosts, sHost)
}
}
// Connect 创建服务器连接
func Connect(server *Server) (*SHost, error) {
if server == nil {
err := fmt.Errorf("server is nil")
log.Println(err)
return nil, nil
}
// get auth method
auth := make([]ssh.AuthMethod, 0)
auth = append(auth, ssh.Password(server.Password))
clientConfig := &ssh.ClientConfig{
User: server.User,
Auth: auth,
Timeout: 30 * time.Second, // 连接超时时间
// 验证服务器的主机密钥 HostKeyCallback:ssh.InsecureIgnoreHostKey(),也可以
HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error {
return nil
},
}
addr := fmt.Sprintf("%s:%d", server.Host, server.Port)
client, err := ssh.Dial("tcp", addr, clientConfig)
if err != nil {
log.Println(err)
return nil, err
}
session, err := client.NewSession()
if err != nil {
log.Println(err)
return nil, err
}
return &SHost{Session: session, Host: server.Host, GoAbPath: server.GoAbPath}, nil
}
func main() {
flag.Parse()
switch *flagGit {
case "-h":
fmt.Println("completely package go get,example goget github.com/gpmgo/gopm")
return
case "":
return
default:
// 是否已经错误
var isErr bool
// 先在本机下载
cmd := exec.Command("cmd", "/C", "go get "+*flagGit)
log.Println(cmd.Args)
stdout, err := cmd.StdoutPipe()
if err != nil {
log.Println(err)
}
stderr, err := cmd.StderrPipe()
if err != nil {
log.Println(err)
isErr = true
}
if err := cmd.Start(); err != nil {
log.Println(err)
isErr = true
}
outBytes, err := ioutil.ReadAll(stdout)
if err != nil {
log.Println(err)
isErr = true
} else {
log.Println(string(outBytes))
}
errBytes, err := ioutil.ReadAll(stderr)
if err != nil {
log.Println(err)
isErr = true
} else {
log.Println(string(errBytes))
}
// 后再服务器上运行
for i := 0; i < len(sHosts); i++ {
sHosts[i].Session.Stderr = os.Stderr
sHosts[i].Session.Stdout = os.Stdout
Shell := fmt.Sprintf(" get -u %s", *flagGit)
log.Println(Shell)
err = sHosts[i].Session.Run("sudo " + sHosts[i].GoAbPath + Shell)
if err != nil {
log.Println(err, "出现错误的服务器的IP地址", sHosts[i].Host)
isErr = true
}
}
if isErr {
log.Println("拉取失败")
}
}
}
;配置文件写入方法 利用配置文件的分组规则写
[servers "A"]
User = 用户名
port = 开放端口号
host = ip
password = 密码
goAbPath = /usr/local/go/bin/go
- 曾经遇到的问题
- go get 下来发现gopath路径下的src文件,没有拉取下来的文件,因为我开了module,即set GO111MODULE=on。所以来下来的代码放到gopath路径下的pkg文件夹下
- 远程调用go命令的时候,他会说不具备go命令。但是自己远程服务器下是有配置的。最后只能通过利用which go获取go命令的绝对路径,进行运行。