parent
4009e845cb
commit
3c02d55ebf
|
@ -9,7 +9,6 @@ import (
|
||||||
"runtime"
|
"runtime"
|
||||||
"os"
|
"os"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const AppVersion = "1.2"
|
const AppVersion = "1.2"
|
||||||
|
@ -18,12 +17,8 @@ func main() {
|
||||||
var serverAddr string
|
var serverAddr string
|
||||||
var allowRoot bool
|
var allowRoot bool
|
||||||
var version bool
|
var version bool
|
||||||
var keyFile string
|
|
||||||
var certFile string
|
|
||||||
flag.BoolVar(&allowRoot, "allow-root", false, "./gocron-node -allow-root")
|
flag.BoolVar(&allowRoot, "allow-root", false, "./gocron-node -allow-root")
|
||||||
flag.StringVar(&serverAddr, "s", "0.0.0.0:5921", "./gocron-node -s ip:port")
|
flag.StringVar(&serverAddr, "s", "0.0.0.0:5921", "./gocron-node -s ip:port")
|
||||||
flag.StringVar(&certFile, "cert-file", "", "./gocron-node -cert-file path")
|
|
||||||
flag.StringVar(&keyFile, "key-file", "", "./gocron-node -key-file path")
|
|
||||||
flag.BoolVar(&version, "v", false, "./gocron-node -v")
|
flag.BoolVar(&version, "v", false, "./gocron-node -v")
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
|
@ -32,25 +27,11 @@ func main() {
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
certFile = strings.TrimSpace(certFile)
|
|
||||||
keyFile = strings.TrimSpace(keyFile)
|
|
||||||
|
|
||||||
if certFile != "" && keyFile == "" {
|
|
||||||
fmt.Println("missing argument key-file")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if keyFile != "" && certFile == "" {
|
|
||||||
fmt.Println("missing argument cert-file")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if runtime.GOOS != "windows" && os.Getuid() == 0 && !allowRoot {
|
if runtime.GOOS != "windows" && os.Getuid() == 0 && !allowRoot {
|
||||||
fmt.Println("Do not run gocron-node as root user")
|
fmt.Println("Do not run gocron-node as root user")
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
server.Start(serverAddr)
|
||||||
server.Start(serverAddr, certFile, keyFile)
|
|
||||||
}
|
}
|
|
@ -10,7 +10,7 @@ import (
|
||||||
"github.com/ouqiang/gocron/cmd"
|
"github.com/ouqiang/gocron/cmd"
|
||||||
)
|
)
|
||||||
|
|
||||||
const AppVersion = "1.2"
|
const AppVersion = "1.1"
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
app := cli.NewApp()
|
app := cli.NewApp()
|
||||||
|
|
|
@ -10,7 +10,6 @@ type Host struct {
|
||||||
Name string `xorm:"varchar(64) notnull"` // 主机名称
|
Name string `xorm:"varchar(64) notnull"` // 主机名称
|
||||||
Alias string `xorm:"varchar(32) notnull default '' "` // 主机别名
|
Alias string `xorm:"varchar(32) notnull default '' "` // 主机别名
|
||||||
Port int `xorm:"notnull default 22"` // 主机端口
|
Port int `xorm:"notnull default 22"` // 主机端口
|
||||||
CertFile string `xorm:"varchar(64) notnull default '' "`
|
|
||||||
Remark string `xorm:"varchar(100) notnull default '' "` // 备注
|
Remark string `xorm:"varchar(100) notnull default '' "` // 备注
|
||||||
BaseModel `xorm:"-"`
|
BaseModel `xorm:"-"`
|
||||||
Selected bool `xorm:"-"`
|
Selected bool `xorm:"-"`
|
||||||
|
@ -27,7 +26,7 @@ func (host *Host) Create() (insertId int16, err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (host *Host) UpdateBean(id int16) (int64, error) {
|
func (host *Host) UpdateBean(id int16) (int64, error) {
|
||||||
return Db.ID(id).Cols("name,alias,port,cert_file,remark").Update(host)
|
return Db.ID(id).Cols("name,alias,port,remark").Update(host)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -49,10 +49,9 @@ func isDatabaseExist(name string) bool {
|
||||||
|
|
||||||
// 迭代升级数据库, 新建表、新增字段等
|
// 迭代升级数据库, 新建表、新增字段等
|
||||||
func (migration *Migration) Upgrade(oldVersionId int) {
|
func (migration *Migration) Upgrade(oldVersionId int) {
|
||||||
versionIds := []int{110, 120}
|
versionIds := []int{110}
|
||||||
upgradeFuncs := []func(*xorm.Session) error {
|
upgradeFuncs := []func(*xorm.Session) error {
|
||||||
migration.upgradeFor110,
|
migration.upgradeFor110,
|
||||||
migration.upgradeFor120,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 默认当前版本为v1.0
|
// 默认当前版本为v1.0
|
||||||
|
@ -136,13 +135,3 @@ func (migration *Migration) upgradeFor110(session *xorm.Session) error {
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// 升级到v1.2版本
|
|
||||||
func (migration *Migration) upgradeFor120(session *xorm.Session) error {
|
|
||||||
// host表增加cert_file字段
|
|
||||||
tableName := TablePrefix + "host"
|
|
||||||
_, err := session.Exec(fmt.Sprintf("ALTER TABLE %s Add COLUMN cert_file VARCHAR(64) NOT NULL DEFAULT ''", tableName))
|
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
|
|
@ -12,7 +12,6 @@ type TaskHostDetail struct {
|
||||||
Name string
|
Name string
|
||||||
Port int
|
Port int
|
||||||
Alias string
|
Alias string
|
||||||
CertFile string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (TaskHostDetail) TableName() string {
|
func (TaskHostDetail) TableName() string {
|
||||||
|
@ -49,7 +48,7 @@ func (th *TaskHost) Add(taskId int, hostIds []int) error {
|
||||||
|
|
||||||
func (th *TaskHost) GetHostIdsByTaskId(taskId int) ([]TaskHostDetail, error) {
|
func (th *TaskHost) GetHostIdsByTaskId(taskId int) ([]TaskHostDetail, error) {
|
||||||
list := make([]TaskHostDetail, 0)
|
list := make([]TaskHostDetail, 0)
|
||||||
fields := "th.id,th.host_id,h.alias,h.name,h.port,h.cert_file"
|
fields := "th.id,th.host_id,h.alias,h.name,h.port"
|
||||||
err := Db.Alias("th").
|
err := Db.Alias("th").
|
||||||
Join("LEFT", hostTableName(), "th.host_id=h.id").
|
Join("LEFT", hostTableName(), "th.host_id=h.id").
|
||||||
Where("th.task_id = ?", taskId).
|
Where("th.task_id = ?", taskId).
|
||||||
|
|
|
@ -16,11 +16,11 @@ var (
|
||||||
errUnavailable = errors.New("无法连接远程服务器")
|
errUnavailable = errors.New("无法连接远程服务器")
|
||||||
)
|
)
|
||||||
|
|
||||||
func ExecWithRetry(ip string, port int, certFile string,taskReq *pb.TaskRequest) (string, error) {
|
func ExecWithRetry(ip string, port int, taskReq *pb.TaskRequest) (string, error) {
|
||||||
tryTimes := 15
|
tryTimes := 60
|
||||||
i := 0
|
i := 0
|
||||||
for i < tryTimes {
|
for i < tryTimes {
|
||||||
output, err := Exec(ip, port, certFile, taskReq)
|
output, err := Exec(ip, port, taskReq)
|
||||||
if err != errUnavailable {
|
if err != errUnavailable {
|
||||||
return output, err
|
return output, err
|
||||||
}
|
}
|
||||||
|
@ -31,14 +31,14 @@ func ExecWithRetry(ip string, port int, certFile string,taskReq *pb.TaskRequest)
|
||||||
return "", errUnavailable
|
return "", errUnavailable
|
||||||
}
|
}
|
||||||
|
|
||||||
func Exec(ip string, port int, certFile string, taskReq *pb.TaskRequest) (string, error) {
|
func Exec(ip string, port int, taskReq *pb.TaskRequest) (string, error) {
|
||||||
defer func() {
|
defer func() {
|
||||||
if err := recover(); err != nil {
|
if err := recover(); err != nil {
|
||||||
logger.Error("panic#rpc/client.go:Exec#", err)
|
logger.Error("panic#rpc/client.go:Exec#", err)
|
||||||
}
|
}
|
||||||
} ()
|
} ()
|
||||||
addr := fmt.Sprintf("%s:%d", ip, port)
|
addr := fmt.Sprintf("%s:%d", ip, port)
|
||||||
conn, err := grpcpool.Pool.Get(addr, certFile)
|
conn, err := grpcpool.Pool.Get(addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,8 +6,6 @@ import (
|
||||||
"time"
|
"time"
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
"errors"
|
"errors"
|
||||||
"google.golang.org/grpc/credentials"
|
|
||||||
"strings"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -32,12 +30,12 @@ type GRPCPool struct {
|
||||||
sync.RWMutex
|
sync.RWMutex
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *GRPCPool) Get(addr, certFile string) (*grpc.ClientConn, error) {
|
func (p *GRPCPool) Get(addr string) (*grpc.ClientConn, error) {
|
||||||
p.RLock()
|
p.RLock()
|
||||||
pool, ok := p.conns[addr]
|
pool, ok := p.conns[addr]
|
||||||
p.RUnlock()
|
p.RUnlock()
|
||||||
if !ok {
|
if !ok {
|
||||||
err := p.newCommonPool(addr, certFile)
|
err := p.newCommonPool(addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -88,7 +86,7 @@ func (p *GRPCPool) ReleaseAll() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 初始化底层连接池
|
// 初始化底层连接池
|
||||||
func (p *GRPCPool) newCommonPool(addr, certFile string) (error) {
|
func (p *GRPCPool) newCommonPool(addr string) (error) {
|
||||||
p.Lock()
|
p.Lock()
|
||||||
defer p.Unlock()
|
defer p.Unlock()
|
||||||
commonPool, ok := p.conns[addr]
|
commonPool, ok := p.conns[addr]
|
||||||
|
@ -99,17 +97,7 @@ func (p *GRPCPool) newCommonPool(addr, certFile string) (error) {
|
||||||
InitialCap: 1,
|
InitialCap: 1,
|
||||||
MaxCap: 30,
|
MaxCap: 30,
|
||||||
Factory: func() (interface{}, error) {
|
Factory: func() (interface{}, error) {
|
||||||
if certFile == "" {
|
return grpc.Dial(addr, grpc.WithInsecure())
|
||||||
return grpc.Dial(addr, grpc.WithInsecure())
|
|
||||||
}
|
|
||||||
|
|
||||||
server := strings.Split(addr, ":")
|
|
||||||
creds, err := credentials.NewClientTLSFromFile(certFile, server[0])
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return grpc.Dial(addr, grpc.WithTransportCredentials(creds))
|
|
||||||
},
|
},
|
||||||
Close: func(v interface{}) error {
|
Close: func(v interface{}) error {
|
||||||
conn, ok := v.(*grpc.ClientConn)
|
conn, ok := v.(*grpc.ClientConn)
|
||||||
|
|
|
@ -7,7 +7,6 @@ import (
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
pb "github.com/ouqiang/gocron/modules/rpc/proto"
|
pb "github.com/ouqiang/gocron/modules/rpc/proto"
|
||||||
"github.com/ouqiang/gocron/modules/utils"
|
"github.com/ouqiang/gocron/modules/utils"
|
||||||
"google.golang.org/grpc/credentials"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Server struct {}
|
type Server struct {}
|
||||||
|
@ -30,7 +29,7 @@ func (s Server) Run(ctx context.Context, req *pb.TaskRequest) (*pb.TaskResponse,
|
||||||
return resp, nil
|
return resp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func Start(addr, certFile, keyFile string) {
|
func Start(addr string) {
|
||||||
defer func() {
|
defer func() {
|
||||||
if err := recover(); err != nil {
|
if err := recover(); err != nil {
|
||||||
grpclog.Println("panic", err)
|
grpclog.Println("panic", err)
|
||||||
|
@ -40,23 +39,9 @@ func Start(addr, certFile, keyFile string) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
grpclog.Fatal(err)
|
grpclog.Fatal(err)
|
||||||
}
|
}
|
||||||
|
s := grpc.NewServer()
|
||||||
var s *grpc.Server
|
pb.RegisterTaskServer(s, Server{})
|
||||||
if certFile != "" {
|
grpclog.Println("listen ", addr)
|
||||||
// TLS认证
|
|
||||||
creds, err := credentials.NewServerTLSFromFile(certFile, keyFile)
|
|
||||||
if err != nil {
|
|
||||||
grpclog.Fatalf("Failed to generate credentials %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
s = grpc.NewServer(grpc.Creds(creds))
|
|
||||||
pb.RegisterTaskServer(s, Server{})
|
|
||||||
grpclog.Printf("listen %s with TLS", addr)
|
|
||||||
} else {
|
|
||||||
s = grpc.NewServer()
|
|
||||||
pb.RegisterTaskServer(s, Server{})
|
|
||||||
grpclog.Println("listen ", addr)
|
|
||||||
}
|
|
||||||
err = s.Serve(l)
|
err = s.Serve(l)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
grpclog.Fatal(err)
|
grpclog.Fatal(err)
|
||||||
|
|
|
@ -64,7 +64,6 @@ type HostForm struct {
|
||||||
Name string `binding:"Required;MaxSize(64)"`
|
Name string `binding:"Required;MaxSize(64)"`
|
||||||
Alias string `binding:"Required;MaxSize(32)"`
|
Alias string `binding:"Required;MaxSize(32)"`
|
||||||
Port int `binding:"Required;Range(1-65535)"`
|
Port int `binding:"Required;Range(1-65535)"`
|
||||||
CertFile string
|
|
||||||
Remark string
|
Remark string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -94,12 +93,6 @@ func Store(ctx *macaron.Context, form HostForm) string {
|
||||||
hostModel.Alias = strings.TrimSpace(form.Alias)
|
hostModel.Alias = strings.TrimSpace(form.Alias)
|
||||||
hostModel.Port = form.Port
|
hostModel.Port = form.Port
|
||||||
hostModel.Remark = strings.TrimSpace(form.Remark)
|
hostModel.Remark = strings.TrimSpace(form.Remark)
|
||||||
hostModel.CertFile = strings.TrimSpace(form.CertFile)
|
|
||||||
|
|
||||||
if hostModel.CertFile != "" && !utils.FileExist(hostModel.CertFile) {
|
|
||||||
return json.CommonFailure("证书文件不存在或无权限访问")
|
|
||||||
}
|
|
||||||
|
|
||||||
isCreate := false
|
isCreate := false
|
||||||
oldHostModel := new(models.Host)
|
oldHostModel := new(models.Host)
|
||||||
err = oldHostModel.Find(int(id))
|
err = oldHostModel.Find(int(id))
|
||||||
|
@ -107,7 +100,6 @@ func Store(ctx *macaron.Context, form HostForm) string {
|
||||||
return json.CommonFailure("主机不存在")
|
return json.CommonFailure("主机不存在")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if id > 0 {
|
if id > 0 {
|
||||||
_, err = hostModel.UpdateBean(id)
|
_, err = hostModel.UpdateBean(id)
|
||||||
} else {
|
} else {
|
||||||
|
@ -120,7 +112,10 @@ func Store(ctx *macaron.Context, form HostForm) string {
|
||||||
|
|
||||||
if !isCreate {
|
if !isCreate {
|
||||||
oldAddr := fmt.Sprintf("%s:%d", oldHostModel.Name, oldHostModel.Port)
|
oldAddr := fmt.Sprintf("%s:%d", oldHostModel.Name, oldHostModel.Port)
|
||||||
grpcpool.Pool.Release(oldAddr)
|
newAddr := fmt.Sprintf("%s:%d", hostModel.Name, hostModel.Port)
|
||||||
|
if oldAddr != newAddr {
|
||||||
|
grpcpool.Pool.Release(oldAddr)
|
||||||
|
}
|
||||||
|
|
||||||
taskModel := new(models.Task)
|
taskModel := new(models.Task)
|
||||||
tasks, err := taskModel.ActiveListByHostId(id)
|
tasks, err := taskModel.ActiveListByHostId(id)
|
||||||
|
@ -180,7 +175,7 @@ func Ping(ctx *macaron.Context) string {
|
||||||
taskReq := &rpc.TaskRequest{}
|
taskReq := &rpc.TaskRequest{}
|
||||||
taskReq.Command = "echo hello"
|
taskReq.Command = "echo hello"
|
||||||
taskReq.Timeout = 10
|
taskReq.Timeout = 10
|
||||||
output, err := client.Exec(hostModel.Name, hostModel.Port, hostModel.CertFile, taskReq)
|
output, err := client.Exec(hostModel.Name, hostModel.Port, taskReq)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return json.CommonFailure("连接失败-" + err.Error() + " " + output, err)
|
return json.CommonFailure("连接失败-" + err.Error() + " " + output, err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -181,7 +181,7 @@ func (h *RPCHandler) Run(taskModel models.Task) (result string, err error) {
|
||||||
var resultChan chan TaskResult = make(chan TaskResult, len(taskModel.Hosts))
|
var resultChan chan TaskResult = make(chan TaskResult, len(taskModel.Hosts))
|
||||||
for _, taskHost := range taskModel.Hosts {
|
for _, taskHost := range taskModel.Hosts {
|
||||||
go func(th models.TaskHostDetail) {
|
go func(th models.TaskHostDetail) {
|
||||||
output, err := rpcClient.ExecWithRetry(th.Name, th.Port, th.CertFile, taskRequest)
|
output, err := rpcClient.ExecWithRetry(th.Name, th.Port, taskRequest)
|
||||||
var errorMessage string = ""
|
var errorMessage string = ""
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorMessage = err.Error()
|
errorMessage = err.Error()
|
||||||
|
|
|
@ -36,15 +36,6 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="two fields">
|
|
||||||
<div class="field">
|
|
||||||
<label>证书路径</label>
|
|
||||||
<div class="ui small input">
|
|
||||||
<input type="text" name="cert_file" value="{{{.Host.CertFile}}}"
|
|
||||||
placeholder="data/certs/server.pem">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="two fields">
|
<div class="two fields">
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label>备注</label>
|
<label>备注</label>
|
||||||
|
|
|
@ -36,7 +36,6 @@
|
||||||
<th>主机名</th>
|
<th>主机名</th>
|
||||||
<th>别名</th>
|
<th>别名</th>
|
||||||
<th>端口</th>
|
<th>端口</th>
|
||||||
<th>证书</th>
|
|
||||||
<th>备注</th>
|
<th>备注</th>
|
||||||
<th>操作</th>
|
<th>操作</th>
|
||||||
</tr>
|
</tr>
|
||||||
|
@ -48,7 +47,6 @@
|
||||||
<td>{{{.Name}}}</td>
|
<td>{{{.Name}}}</td>
|
||||||
<td>{{{.Alias}}}</td>
|
<td>{{{.Alias}}}</td>
|
||||||
<td>{{{.Port}}}</td>
|
<td>{{{.Port}}}</td>
|
||||||
<td>{{{.CertFile}}}</td>
|
|
||||||
<td>{{{.Remark}}}</td>
|
<td>{{{.Remark}}}</td>
|
||||||
<td class="operation">
|
<td class="operation">
|
||||||
<a class="ui purple button" href="/host/edit/{{{.Id}}}">编辑</a>
|
<a class="ui purple button" href="/host/edit/{{{.Id}}}">编辑</a>
|
||||||
|
|
Loading…
Reference in New Issue