diff --git a/consts.go b/consts.go index fe248bc..cbc1aa4 100644 --- a/consts.go +++ b/consts.go @@ -1,8 +1,6 @@ package main const ( - programName = "gron" - defaultConfigFileName = "gron.conf" defaultOnSuccessMessageFmt = "Job {{.JobName}} finished." diff --git a/go.mod b/go.mod index 57dd24f..5e586a9 100644 --- a/go.mod +++ b/go.mod @@ -3,14 +3,15 @@ module github.com/nxshock/gron go 1.18 require ( - github.com/BurntSushi/toml v1.2.0 + github.com/BurntSushi/toml v1.2.1 github.com/antonfisher/nested-logrus-formatter v1.3.1 github.com/creasty/defaults v1.6.0 github.com/denisenkom/go-mssqldb v0.12.3 + github.com/gorilla/websocket v1.5.0 github.com/jackc/pgx v3.6.2+incompatible github.com/nxshock/logwriter v0.0.0-20220514172136-b1385d4106de github.com/robfig/cron/v3 v3.0.1 - github.com/sijms/go-ora/v2 v2.5.3 + github.com/sijms/go-ora/v2 v2.5.6 github.com/sirupsen/logrus v1.9.0 github.com/stretchr/testify v1.8.0 ) @@ -28,8 +29,8 @@ require ( github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/shopspring/decimal v1.3.1 // indirect - golang.org/x/crypto v0.1.0 // indirect - golang.org/x/sys v0.1.0 // indirect + golang.org/x/crypto v0.3.0 // indirect + golang.org/x/sys v0.2.0 // indirect golang.org/x/text v0.4.0 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/go.sum b/go.sum index 8694b4a..de5e9ae 100644 --- a/go.sum +++ b/go.sum @@ -1,8 +1,8 @@ github.com/Azure/azure-sdk-for-go/sdk/azcore v0.19.0/go.mod h1:h6H6c8enJmmocHUbLiiGY6sx7f9i+X3m1CHdd5c6Rdw= github.com/Azure/azure-sdk-for-go/sdk/azidentity v0.11.0/go.mod h1:HcM1YX14R7CJcghJGOYCgdezslRSVzqwLf/q+4Y2r/0= github.com/Azure/azure-sdk-for-go/sdk/internal v0.7.0/go.mod h1:yqy467j36fJxcRV2TzfVZ1pCb5vxm4BtZPUdYWe/Xo8= -github.com/BurntSushi/toml v1.2.0 h1:Rt8g24XnyGTyglgET/PRUNlrUeu9F5L+7FilkXfZgs0= -github.com/BurntSushi/toml v1.2.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak= +github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/antonfisher/nested-logrus-formatter v1.3.1 h1:NFJIr+pzwv5QLHTPyKz9UMEoHck02Q9L0FP13b/xSbQ= github.com/antonfisher/nested-logrus-formatter v1.3.1/go.mod h1:6WTfyWFkBc9+zyBaKIqRrg/KwMqBbodBjgbHjDz7zjA= github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I= @@ -23,6 +23,8 @@ github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 h1:au07oEsX2xN0kt github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A= github.com/golang-sql/sqlexp v0.1.0/go.mod h1:J4ad9Vo8ZCWQ2GMrC4UCQy1JpCbwU9m3EOqtpKwwwHI= +github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= +github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/jackc/fake v0.0.0-20150926172116-812a484cc733 h1:vr3AYkKovP8uR8AvSGGUK1IDqRa5lAAvEkZG1LKaCRc= github.com/jackc/fake v0.0.0-20150926172116-812a484cc733/go.mod h1:WrMFNQdiFJ80sQsxDoMokWK1W5TQtxBFNpzWTD84ibQ= github.com/jackc/pgx v3.6.2+incompatible h1:2zP5OD7kiyR3xzRYMhOcXVvkDZsImVXfj+yIyTQf3/o= @@ -53,8 +55,8 @@ github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBO github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8= github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= -github.com/sijms/go-ora/v2 v2.5.3 h1:klGKmhqRONVTtIzTdfYTvrW94kdJkdmZl93u2A3vchI= -github.com/sijms/go-ora/v2 v2.5.3/go.mod h1:EHxlY6x7y9HAsdfumurRfTd+v8NrEOTR3Xl4FWlH6xk= +github.com/sijms/go-ora/v2 v2.5.6 h1:V53uhbcVpPrGBICnwBJx780+lcqfbTWLCi376B7Dr5A= +github.com/sijms/go-ora/v2 v2.5.6/go.mod h1:EHxlY6x7y9HAsdfumurRfTd+v8NrEOTR3Xl4FWlH6xk= github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -66,8 +68,8 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.1.0 h1:MDRAIl0xIo9Io2xV565hzXHw3zVseKrJKodhohM5CjU= -golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= +golang.org/x/crypto v0.3.0 h1:a06MkbcxBrEFc0w0QIZWXrH/9cCX6KJyWbBOIwAn+7A= +golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20210610132358-84b48f89b13b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= @@ -78,8 +80,8 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U= -golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A= +golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= diff --git a/httpserver.go b/httpserver.go index fb2fbf7..3d1edc3 100644 --- a/httpserver.go +++ b/httpserver.go @@ -8,11 +8,49 @@ import ( "net" "net/http" "sort" + "sync" "time" + "github.com/gorilla/websocket" log "github.com/sirupsen/logrus" ) +type WsConnections struct { + connections map[*websocket.Conn]struct{} + mutex sync.Mutex +} + +func (wc *WsConnections) Add(c *websocket.Conn) { + wc.mutex.Lock() + defer wc.mutex.Unlock() + + wc.connections[c] = struct{}{} +} + +func (wc *WsConnections) Delete(c *websocket.Conn) { + wc.mutex.Lock() + defer wc.mutex.Unlock() + + delete(wc.connections, c) +} + +func (wc *WsConnections) Send(message interface{}) { + for conn := range wc.connections { + go func(conn *websocket.Conn) { _ = conn.WriteJSON(message) }(conn) + } +} + +var upgrader = websocket.Upgrader{ + ReadBufferSize: 1024, + WriteBufferSize: 1024, + CheckOrigin: func(r *http.Request) bool { + return true + }, +} + +var wsConnections = &WsConnections{ + connections: make(map[*websocket.Conn]struct{})} + func httpServer(listenAddress string) { if listenAddress == "none" { return @@ -23,6 +61,7 @@ func httpServer(listenAddress string) { http.HandleFunc("/shutdown", handleShutdown) http.HandleFunc("/start", handleForceStart) http.HandleFunc("/details", handleDetails) + http.HandleFunc("/ws", handleWebSocket) log.WithField("job", "http_server").Fatal(http.ListenAndServe(listenAddress, nil)) } @@ -39,10 +78,9 @@ func handler(w http.ResponseWriter, r *http.Request) { globalMutex.RLock() buf := new(bytes.Buffer) - jobEntries := kernel.c.Entries() jobs := make(map[string][]*Job) - for _, jobEntry := range jobEntries { + for _, jobEntry := range kernel.c.Entries() { job := jobEntry.Job.(*Job) job.NextLaunch = jobEntry.Next.Format(config.TimeFormat) jobs[job.JobConfig.Category] = append(jobs[job.JobConfig.Category], job) @@ -73,6 +111,41 @@ func handler(w http.ResponseWriter, r *http.Request) { _, _ = buf.WriteTo(w) } +func handleWebSocket(w http.ResponseWriter, r *http.Request) { + conn, err := upgrader.Upgrade(w, r, nil) + if err != nil { + return + } + + wsConnections.Add(conn) + defer wsConnections.Delete(conn) + + var startMessage struct { + JobName string + } + + for { + err := conn.ReadJSON(&startMessage) + if err != nil { + log.Println(err) + break + } + + for _, jobEntry := range kernel.c.Entries() { + job := jobEntry.Job.(*Job) + if job.Name == startMessage.JobName { + host, _, err := net.SplitHostPort(conn.RemoteAddr().String()) + if err != nil { + host = r.RemoteAddr + } + log.WithField("job", "http_server").Printf("Forced start %s from %s.", job.Name, host) + go job.Run() + break + } + } + } +} + func handleForceStart(w http.ResponseWriter, r *http.Request) { jobName := r.FormValue("jobName") if jobName == "" { @@ -80,9 +153,7 @@ func handleForceStart(w http.ResponseWriter, r *http.Request) { return } - jobEntries := kernel.c.Entries() - - for _, jobEntry := range jobEntries { + for _, jobEntry := range kernel.c.Entries() { job := jobEntry.Job.(*Job) if job.Name == jobName { host, _, err := net.SplitHostPort(r.RemoteAddr) diff --git a/job.go b/job.go index 21691fb..07ab288 100644 --- a/job.go +++ b/job.go @@ -128,6 +128,16 @@ func (j *Job) openAndMergeLog() (logEntry *log.Entry, jobLogFile *os.File) { } func (j *Job) Run() { + // TODO: переписать неоптимальный цикл + for _, jobEntry := range kernel.c.Entries() { + if jobEntry.Job.(*Job) != j { + continue + } + + j.NextLaunch = jobEntry.Next.Format(config.TimeFormat) + break + } + log, jobLogFile := j.openAndMergeLog() defer jobLogFile.Close() @@ -165,6 +175,8 @@ func (j *Job) runTry(log *log.Entry, jobLogFile *os.File) error { j.LastStartTime = startTime.Format(config.TimeFormat) globalMutex.Unlock() + wsConnections.Send(j) + var err error switch j.JobConfig.Type { case Cmd: @@ -196,6 +208,8 @@ func (j *Job) runTry(log *log.Entry, jobLogFile *os.File) error { j.LastExecutionDuration = endTime.Sub(startTime).Truncate(time.Second).String() globalMutex.Unlock() + wsConnections.Send(j) + return err } @@ -318,12 +332,5 @@ func (j *Job) errorMessage(err error) string { } func runSimpleCmd(command string, args ...string) error { - log.Println(command) - log.Println(args) - err := exec.Command(command, args...).Run() - if err != nil { - log.Println(">>", err) - } - - return err + return exec.Command(command, args...).Run() } diff --git a/strutils.go b/strutils.go index 994d16f..a7554c4 100644 --- a/strutils.go +++ b/strutils.go @@ -8,6 +8,6 @@ import ( func format(fmt string, v interface{}) string { t := new(template.Template) b := new(strings.Builder) - template.Must(t.Parse(fmt)).Execute(b, v) // TODO: обработать возможные ошибки + _ = template.Must(t.Parse(fmt)).Execute(b, v) // TODO: обработать возможные ошибки return b.String() } diff --git a/webui/index.htm b/webui/index.htm index 673a867..defdeed 100644 --- a/webui/index.htm +++ b/webui/index.htm @@ -6,6 +6,7 @@