关闭

分享一个小东西,自动到指定的地方下载动态版本的命令,然后根据配置循环执行调用.

标签: gogolanggolang1.5Go语言自动执行命令
1427人阅读 评论(0) 收藏 举报
分类:

写此小程序的原因是,研究小米的open-falcon的时候用他们的插件管理感觉不适合我们公司,就自动动手写了个基于服务端的版本控制的,命令下载时基于http的所以也比较方便.

代码写的比较紧急有点乱,先这样吧,回头用起来再调优一下.

先发下执行的结果:

开始解析cfg.json配置文件.解析配置文件成功:  {gyc D:/code/20151117/src/ssh true
27.0.0.1:2789 http://127.0.0.1:1789/pkg/ :1789 true 60}
开始初始化本地命令...
初始化本地命令完成...
[{client.go D:\code\20151117\src\ssh\client.go 256034f7168a3937b124ad89227f0ea9
 {sshd D:\code\20151117\src\ssh\sshd af7ec72582c33ecd97dc5abf0e747e92}]
D:/code/20151117/src/ssh
2015-11-18 17:46:29获取服务端版本!
{fmt.exe fe117de8dbf1a460e66d2a799bde17cf 30 }
2015-11-18 17:46:29开始验证本地!
{Test01.exe e6a54c6f478d93828cb635e97d710ba6 30 }
2015-11-18 17:46:29开始验证本地!
开始下载:http://127.0.0.1:1789/pkg/fmt.exe
开始下载:http://127.0.0.1:1789/pkg/Test01.exe
tmp/Test01.exe D:/code/20151117/src/ssh/Test01.exe
tmp/fmt.exe D:/code/20151117/src/ssh/fmt.exe
2015-11-18 17:46:29 开始执行Test01.exe
2015-11-18 17:46:29 开始执行fmt.exe
命令Test01.exe结果:This is Test01
命令fmt.exe结果:2015-11-18 17:46:29.3219182 +0800 CST
命令Test01.exe结果:This is Test01
命令fmt.exe结果:2015-11-18 17:46:59.3536359 +0800 CST
2015-11-18 17:47:29获取服务端版本!
清除过期的命令:Test01.exe.
{fmt.exe fe117de8dbf1a460e66d2a799bde17cf 30 }
2015-11-18 17:47:29 退出执行Test01.exe
命令fmt.exe结果:2015-11-18 17:47:29.3933541 +0800 CST

废话不多说直接上源码:

服务端简单的代码小例子:

server.go

package main

import (
	"encoding/json"
	"fmt"
	"net"
)

type versionInfo struct {
	Name     string `json:name`
	Md5      string `json:md5`
	Interval int    `json:interval`
	Args     string `json:args`
}

//测试代码.服务端可以自己按需求定义.
func main() {
	var list []versionInfo
	list = []versionInfo{{"fmt.exe", "fe117de8dbf1a460e66d2a799bde17cf", 30, ""},
		{"Test01.exe", "e6a54c6f478d93828cb635e97d710ba6", 30, ""}}
	//这些内容命令版本多的时候可以用数据库来控制,不多就随便自己搞搞吧,
	b, err := json.Marshal(list)
	if err != nil {
		fmt.Printf("初始话数据失败:%s\n", err)
		return
	}
	lis, err := net.Listen("tcp", ":2789")
	if err != nil {
		fmt.Printf("初始化服务端失败:%s\n", err)
		return
	}
	defer lis.Close()
	for {
		con, err := lis.Accept()
		if err != nil {
			fmt.Println(err)
			continue
		}
		go func(con net.Conn) {
			defer con.Close()
			buf := make([]byte, 512)
			n, err := con.Read(buf)
			if err != nil {
				fmt.Println(err)
				return
			}
			if string(buf[:n]) != "gyc" { //这里的值需要根据自己来控制,比如每次收到的请求到数据库查询来取版本返回.
				con.Write([]byte("No this group!"))
				return
			}
			con.Write(b)
		}(con)
	}
}

下面是每个实例的代码.

我得用目录是:new

getServerVersion.go

package new

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net"
	"os"
	"time"
)

func GetVersion(group string) []RemoteVersionInfo {
	var list []RemoteVersionInfo
This:
	con, err := net.Dial("tcp", Getconfig().GetVersionServer)
	if err != nil {
		fmt.Printf("%s 从%s获取命令版本信息错误: %s\n", GetNowTime(), Getconfig().GetVersionServer, err)
		time.Sleep(60e9)
		goto This
	}
	defer con.Close()
	_, err = con.Write([]byte(group))
	if err != nil {
		fmt.Printf("发送%s的version请求失败:%s\n", group, err)
		time.Sleep(60e9)
		goto This
	}
	buf, err := ioutil.ReadAll(con)
	if err != nil {
		fmt.Printf("%s 获取命令版本信息错误: %s\n", GetNowTime(), err)
		time.Sleep(60e9)
		goto This
	}
	if string(buf) == "No this group!" {
		fmt.Printf("版本库找不到此分组:%s\n", group)
		os.Exit(1)
	}
	err = json.Unmarshal(buf, &list)
	if err != nil {
		fmt.Printf("%s 解析获取的版本消息错误: %s\n", GetNowTime(), err)
		time.Sleep(60e9)
		goto This
	}
	return list
}
init_var.go
package new

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
	"sync"
)

var CmdPath string
var config ConfigJson
var Lock *sync.RWMutex = new(sync.RWMutex)
var CmdFuncMap *FuncMap

func Getconfig() *ConfigJson {
	Lock.RLock()
	defer Lock.RUnlock()
	return &config
}

func init() {
	err := parseConfig("cfg.json")
	if err != nil {
		os.Exit(1)
	}
	info, err := os.Lstat(Getconfig().CmdDirPath)
	CmdPath = Getconfig().CmdDirPath
	if err != nil || !info.IsDir() {
		fmt.Printf("校验目录失败: %s \n", err)
		err := initDir()
		if err != nil {
			os.Exit(1)
		}
		fmt.Printf("使用默认配置做根目录: %s\n", CmdPath)
	}
	initBaseCmdInfo(CmdPath)
	CmdFuncMap = &FuncMap{make(map[string]*ExecCmdInfo), new(sync.RWMutex)}
}

func parseConfig(configPath string) error {
	fmt.Printf("开始解析%s配置文件.", configPath)
	b, err := ioutil.ReadFile(configPath)
	if err != nil {
		fmt.Printf("读取配置文件出错: %s\n", err)
		return err
	}
	err = json.Unmarshal(b, &config)
	if err != nil {
		fmt.Printf("解析配置出错: %s\n", err)
		return err
	}
	fmt.Println("解析配置文件成功: ", config)
	return nil
}

func initDir() error {
	fmt.Println("初始化默认路径.")
	p, err := os.Getwd()
	if err != nil {
		fmt.Printf("获取当前目录错误: %s\n", err)
		return err
	}
	info, err := os.Lstat("cmd")
	if err == nil && info.IsDir() {
		CmdPath = strings.Replace(p, `\`, `/`, 20) + `/cmd`
		return nil
	}
	err = os.Mkdir("cmd", 0644)
	if err != nil {
		fmt.Printf("初始化文件夹失败: %s\n", err)
		return err
	}
	CmdPath = strings.Replace(p, `\`, `/`, 20) + `/cmd`
	return nil
}

func initBaseCmdInfo(path string) {
	fmt.Println("开始初始化本地命令...")
	err := filepath.Walk(path, run)
	if err != nil {
		fmt.Printf("%s 初始化命令列表出错: %s", GetNowTime(), err)
		os.Exit(1)
	}
	fmt.Println("初始化本地命令完成...")
}
run.go

package new

import (
	"fmt"
	"os/exec"
	"strings"
	"time"
)

func R() {
	if Getconfig().Debug {
		fmt.Printf("%s获取服务端版本!\n", GetNowTime())
	}
	list := GetVersion(Getconfig().Group)
	CmdFuncMap.Lock.Lock()
	for k, m := range CmdFuncMap.Map {
		if !Contain(k, list) {
			fmt.Printf("清除过期的命令:%s.\n", k)
			m.Exit <- true
			delete(CmdFuncMap.Map, k)
		}
	}
	CmdFuncMap.Lock.Unlock()
	for _, cmdVersion := range list {
		fmt.Println(cmdVersion)
		CmdFuncMap.Lock.RLock()
		k, ok := CmdFuncMap.Map[cmdVersion.Name]
		CmdFuncMap.Lock.RUnlock()
		if ok {
			if k.M5 == cmdVersion.Md5 {
				continue
			}
			loadcmdbaseinfo := LoadCmdBaseInfo{k.Name, k.Path, k.M5}
			go Update(loadcmdbaseinfo, cmdVersion)
			continue
		}
		cmd := index(cmdVersion)
		if cmd != nil {
			CmdFuncMap.Lock.Lock()
			CmdFuncMap.Map[cmd.Name] = cmd
			go cmd.Run()
			CmdFuncMap.Lock.Unlock()
		}
	}
}

func index(r RemoteVersionInfo) *ExecCmdInfo {
	if Getconfig().Debug {
		fmt.Printf("%s开始验证本地!\n", GetNowTime())
	}
	var g chan bool = make(chan bool, 1)
	for _, v := range LocalCmdList {
		if v.Name == r.Name && v.Md5 == r.Md5 {
			return &ExecCmdInfo{r.Name, v.Path, r.Md5, r.Interval, split(r.Args), g}
		}
	}
	v := LoadCmdBaseInfo{r.Name, fmt.Sprintf("%s/%s", CmdPath, r.Name), r.Md5}
	go Update(v, r)
	return nil
}

func Update(v LoadCmdBaseInfo, r RemoteVersionInfo) {
	var url string = Getconfig().GetCmdAddr
	if !strings.HasSuffix(url, "/") {
		url = url + "/"
	}
	url = url + r.Name
	str := Download(r.Name, url)
	if str != strings.ToLower(r.Md5) {
		return
	}
	CmdFuncMap.Lock.Lock()
	defer CmdFuncMap.Lock.Unlock()
	k, ok := CmdFuncMap.Map[r.Name]
	if ok {
		k.Exit <- true
		time.Sleep(1e9)
	}
	if !MoveFile(fmt.Sprintf("%s/%s", "tmp", r.Name), v.Path) {
		return
	}
	var g chan bool = make(chan bool, 1)
	E := &ExecCmdInfo{r.Name, v.Path, r.Md5, r.Interval, split(r.Args), g}
	CmdFuncMap.Map[r.Name] = E
	go E.Run()
}

func (this *ExecCmdInfo) Run() {
	fmt.Printf("%s 开始执行%s\n", GetNowTime(), this.Name)
	var exit bool = false
	go func() {
		b := <-this.Exit
		if b {
			exit = b
		}
	}()
	for {
		if exit {
			break
		}
		cmd := exec.Command(this.Path, this.Args...)
		//err := cmd.Run()
		b, err := cmd.Output()
		if err != nil {
			fmt.Printf("执行%s出错:%s\n", this.Name, err)
		}
		if Getconfig().Debug {
			fmt.Printf("命令%s结果:%s", this.Name, string(b))
		}
		time.Sleep(time.Second * time.Duration(this.Interval))
	}
	fmt.Printf("%s 退出执行%s\n", GetNowTime(), this.Name)
}


type.go

package new

import "sync"

type ConfigJson struct {
	Group            string `json:group`
	CmdDirPath       string `json:cmddirpath`
	AutoUpdate       bool   `json:autoupdate`
	GetVersionServer string `json:vetversionserver`
	GetCmdAddr       string `json:getcmdaddr`
	Listen           string `json:listen`
	Debug            bool   `json:debug`
	Update           int    `json:update`
}

type RemoteVersionInfo struct {
	Name     string `json:name`
	Md5      string `json:md5`
	Interval int    `json:interval`
	Args     string `json:args`
}

type ExecCmdInfo struct {
	Name     string
	Path     string
	M5       string
	Interval int
	Args     []string
	Exit     chan bool
}

type LoadCmdBaseInfo struct {
	Name string
	Path string
	Md5  string
}

type FuncMap struct {
	Map  map[string]*ExecCmdInfo
	Lock *sync.RWMutex
}

usefunc.go

package new

import (
	"crypto/md5"
	"fmt"
	"io"
	"net/http"
	"os"
	"strings"
	"time"
)

var LocalCmdList []LoadCmdBaseInfo

func run(path string, info os.FileInfo, err error) error {
	if err != nil {
		return err
	}
	if info.IsDir() {
		return nil
	}

	m5 := Md5(path)
	if m5 != "" {
		LocalCmdList = append(LocalCmdList, LoadCmdBaseInfo{info.Name(), path, m5})
	}
	return nil
}

func Md5(path string) string {
	File, err := os.Open(path)
	if err != nil {
		fmt.Printf("%s 校验%s的md5出错:%s\n", GetNowTime(), path, err)
		return ""
	}
	M := md5.New()
	io.Copy(M, File)
	b := M.Sum([]byte{})
	return fmt.Sprintf("%x", b)
}

func GetNowTime() string {
	return time.Now().Format("2006-01-02 15:04:05")
}

func Download(name, url string) string {
	resp, err := http.Get(url)
	fmt.Printf("开始下载:%s\n", url)
	if err != nil || resp.StatusCode != 200 {
		fmt.Printf("%s 下载%s出错: %s\n", GetNowTime(), url, err)
		return ""
	}
	os.Mkdir("tmp", 0644)
	defer resp.Body.Close()
	name = "tmp/" + name
	File, err := os.Create(name)
	if err != nil {
		fmt.Printf("%s 创建文件%s错误: %s\n", GetNowTime(), name, err)
		return ""
	}
	io.Copy(File, resp.Body)
	File.Close()
	return Md5(name)
}
func split(str string) []string {
	var l []string
	list := strings.Split(str, " ")
	for _, v := range list {
		if len(v) == 0 {
			continue
		}
		if strings.Contains(v, "	") {
			list := strings.Split(v, "	")
			for _, v := range list {
				if len(v) == 0 {
					continue
				}
				l = append(l, v)
			}
			continue
		}
		l = append(l, v)
	}
	return l
}

func MoveFile(s, d string) bool {
	fmt.Println(s, d)
	sFile, err := os.Open(s)
	if err != nil {
		fmt.Printf("移动文件%s出错:%s\n", s, err)
		return false
	}
	defer sFile.Close()
	dFile, err := os.Create(d)
	if err != nil {
		fmt.Printf(" 新建文件%s出错:%s\n", s, err)
		return false
	}
	defer dFile.Close()
	io.Copy(dFile, sFile)
	return true
}

func Contain(k string, list []RemoteVersionInfo) bool {
	for _, v := range list {
		if k == v.Name {
			return true
		}
	}
	return false
}


下面是入口函数:

main.go

package main

import (
	"fmt"
	"new"
	"time"
)

func main() {
	j := new.Getconfig()
	go func() {
		for {
			new.R()
			time.Sleep(time.Second * time.Duration(j.Update))
		}
	}()
	select {}
}
下面是:实例的配置文件:cfg.json

{"Group":"gyc",
"CmdDirPath":"D:/code/201508221",
"AutoUpdate":true,
"GetVersionServer":"127.0.0.1:2789",
"GetCmdAddr":"http://127.0.0.1:1789/pkg/",
"Listen":":1789",
"Debug":true,
"Update":60}




1
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:176148次
    • 积分:3373
    • 等级:
    • 排名:第10608名
    • 原创:157篇
    • 转载:2篇
    • 译文:1篇
    • 评论:33条
    文章分类
    最新评论