强化go get命令

强化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命令的绝对路径,进行运行。