您的位置:首页 > 编程语言 > Go语言

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

2015-11-18 17:37 806 查看
写此小程序的原因是,研究小米的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}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息