limit run command to single instance
parent
091c026a46
commit
eddd813846
|
@ -34,14 +34,17 @@ var Ext2Mode map[string]string = map[string]string{
|
||||||
}
|
}
|
||||||
|
|
||||||
type Action struct {
|
type Action struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Cmd string `json:"cmd"`
|
Cmd string `json:"cmd"`
|
||||||
|
run chan struct{} `json:"-"`
|
||||||
|
pid int `json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Integration struct {
|
type Integration struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Description string `json:"description"`
|
Description string `json:"description"`
|
||||||
Cmd func() (string, error) `json:"-"`
|
Cmd func() (string, error) `json:"-"`
|
||||||
|
run chan struct{} `json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ConfigUI struct {
|
type ConfigUI struct {
|
||||||
|
|
36
file.go
36
file.go
|
@ -1,7 +1,9 @@
|
||||||
package configui
|
package configui
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
@ -23,8 +25,10 @@ type File struct {
|
||||||
// used for parsing post data
|
// used for parsing post data
|
||||||
Data string `json:"data,omitempty"`
|
Data string `json:"data,omitempty"`
|
||||||
|
|
||||||
lock sync.RWMutex `json:"-"`
|
lock sync.RWMutex `json:"-"`
|
||||||
owner *ConfigUI `json:"-"`
|
owner *ConfigUI `json:"-"`
|
||||||
|
run chan struct{} `json:"-"`
|
||||||
|
pid int `json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *File) Read() ([]byte, error) {
|
func (f *File) Read() ([]byte, error) {
|
||||||
|
@ -57,6 +61,20 @@ func (f *File) Do(CmdTimeout time.Duration) (string, error) {
|
||||||
}
|
}
|
||||||
f.lock.RLock()
|
f.lock.RLock()
|
||||||
defer f.lock.RUnlock()
|
defer f.lock.RUnlock()
|
||||||
|
|
||||||
|
// limit running instance to 1
|
||||||
|
if cap(f.run) == 0 {
|
||||||
|
f.run = make(chan struct{}, 1)
|
||||||
|
}
|
||||||
|
select {
|
||||||
|
case f.run <- struct{}{}:
|
||||||
|
defer func() { <-f.run }()
|
||||||
|
default:
|
||||||
|
f.owner.log.Error("task is running: ", f.Name)
|
||||||
|
return "", fmt.Errorf("another task of %s is running with pid: %d", f.Name, f.pid)
|
||||||
|
}
|
||||||
|
|
||||||
|
// prepare cmd
|
||||||
cmd := &exec.Cmd{}
|
cmd := &exec.Cmd{}
|
||||||
if runtime.GOOS == "windows" {
|
if runtime.GOOS == "windows" {
|
||||||
cmd = exec.Command(WIN_SHELL, "/c", f.Action)
|
cmd = exec.Command(WIN_SHELL, "/c", f.Action)
|
||||||
|
@ -66,12 +84,14 @@ func (f *File) Do(CmdTimeout time.Duration) (string, error) {
|
||||||
f.owner.log.Info("DO: ", f.Action)
|
f.owner.log.Info("DO: ", f.Action)
|
||||||
done := make(chan string, 1)
|
done := make(chan string, 1)
|
||||||
go func() {
|
go func() {
|
||||||
out, _ := cmd.CombinedOutput()
|
|
||||||
// real cmd err is unhandled, but passed to client
|
var b bytes.Buffer
|
||||||
// if err != nil {
|
cmd.Stdout = &b
|
||||||
// return string(out), err
|
cmd.Stderr = &b
|
||||||
// }
|
cmd.Start()
|
||||||
done <- string(out)
|
f.pid = cmd.Process.Pid
|
||||||
|
cmd.Wait()
|
||||||
|
done <- b.String()
|
||||||
}()
|
}()
|
||||||
select {
|
select {
|
||||||
case <-time.After(CmdTimeout):
|
case <-time.After(CmdTimeout):
|
||||||
|
|
33
handler.go
33
handler.go
|
@ -8,6 +8,7 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
"kumoly.io/lib/ksrv"
|
"kumoly.io/lib/ksrv"
|
||||||
)
|
)
|
||||||
|
@ -130,9 +131,25 @@ func (cui *ConfigUI) Apply(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
func (cui *ConfigUI) DoAction(w http.ResponseWriter, r *http.Request) {
|
func (cui *ConfigUI) DoAction(w http.ResponseWriter, r *http.Request) {
|
||||||
name := r.URL.Query().Get("name")
|
name := r.URL.Query().Get("name")
|
||||||
for _, v := range cui.Actions {
|
for i, v := range cui.Actions {
|
||||||
if v.Name == name {
|
if v.Name == name {
|
||||||
|
|
||||||
|
// limit running instance to one
|
||||||
|
if cap(cui.Actions[i].run) != 1 {
|
||||||
|
cui.Actions[i].run = make(chan struct{}, 1)
|
||||||
|
}
|
||||||
|
select {
|
||||||
|
case cui.Actions[i].run <- struct{}{}:
|
||||||
|
defer func() { <-cui.Actions[i].run }()
|
||||||
|
default:
|
||||||
|
panic(fmt.Errorf("another task of %s is running with pid: %d", name, v.pid))
|
||||||
|
}
|
||||||
|
|
||||||
file := &File{Name: name, Action: v.Cmd, owner: cui}
|
file := &File{Name: name, Action: v.Cmd, owner: cui}
|
||||||
|
go func() {
|
||||||
|
<-time.After(time.Millisecond * 10)
|
||||||
|
cui.Actions[i].pid = file.pid
|
||||||
|
}()
|
||||||
result, err := file.Do(cui.cmdTimeout)
|
result, err := file.Do(cui.cmdTimeout)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
|
@ -146,8 +163,20 @@ func (cui *ConfigUI) DoAction(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
func (cui *ConfigUI) DoIntegration(w http.ResponseWriter, r *http.Request) {
|
func (cui *ConfigUI) DoIntegration(w http.ResponseWriter, r *http.Request) {
|
||||||
name := r.URL.Query().Get("name")
|
name := r.URL.Query().Get("name")
|
||||||
for _, v := range cui.Integrations {
|
for i, v := range cui.Integrations {
|
||||||
if v.Name == name {
|
if v.Name == name {
|
||||||
|
|
||||||
|
// limit running instance to one
|
||||||
|
if cap(cui.Integrations[i].run) != 1 {
|
||||||
|
cui.Integrations[i].run = make(chan struct{}, 1)
|
||||||
|
}
|
||||||
|
select {
|
||||||
|
case cui.Integrations[i].run <- struct{}{}:
|
||||||
|
defer func() { <-cui.Integrations[i].run }()
|
||||||
|
default:
|
||||||
|
panic(fmt.Errorf("another task of %s is running", name))
|
||||||
|
}
|
||||||
|
|
||||||
result, err := v.Cmd()
|
result, err := v.Cmd()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
|
|
Loading…
Reference in New Issue