limit run command to single instance
							parent
							
								
									091c026a46
								
							
						
					
					
						commit
						eddd813846
					
				| 
						 | 
					@ -36,12 +36,15 @@ 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 {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										32
									
								
								file.go
								
								
								
								
							
							
						
						
									
										32
									
								
								file.go
								
								
								
								
							| 
						 | 
					@ -1,7 +1,9 @@
 | 
				
			||||||
package configui
 | 
					package configui
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"bytes"
 | 
				
			||||||
	"errors"
 | 
						"errors"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
	"os"
 | 
						"os"
 | 
				
			||||||
	"os/exec"
 | 
						"os/exec"
 | 
				
			||||||
	"runtime"
 | 
						"runtime"
 | 
				
			||||||
| 
						 | 
					@ -25,6 +27,8 @@ type File struct {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	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