強化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,並且能夠FQ
// 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命令的絕對路徑,進行運行。