1
0
Fork 0
cmdui/backend/runner/commands.go

118 lines
2.6 KiB
Go

package runner
import (
"bytes"
"context"
"encoding/json"
"fmt"
"io"
"os/exec"
"time"
"github.com/juju/errors"
"github.com/localhots/cmdui/backend/commands"
"github.com/localhots/cmdui/backend/config"
"github.com/localhots/cmdui/backend/db"
"github.com/localhots/cmdui/backend/log"
)
func Start(cmd commands.Command, user db.User) (*Process, error) {
job := db.NewJob(cmd, user)
if err := job.Create(); err != nil {
return nil, errors.Annotate(err, "Failed to create a job")
}
p := &Process{
ID: job.ID,
Job: &job,
}
basePath := config.Get().Commands.BasePath
p.exec = exec.Command(basePath, cmd.CombinedArgs()...)
fd, err := p.useLogfile(p.logfile())
if err != nil {
return nil, err
}
p.exec.Stdout = fd
p.exec.Stderr = fd
if err := pool.add(context.Background(), p); err != nil {
return p, errors.Annotate(err, "Failed to start a process")
}
return p, nil
}
func ReadLogUpdates(ctx context.Context, p *Process, w io.Writer) (done <-chan struct{}, err error) {
cmde := exec.CommandContext(ctx, "/usr/bin/tail", "-n", "100", "-f", p.logfile())
return readLog(ctx, cmde, p, w)
}
func ReadFullLog(ctx context.Context, p *Process, w io.Writer) (done <-chan struct{}, err error) {
cmde := exec.CommandContext(ctx, "/bin/cat", p.logfile())
return readLog(ctx, cmde, p, w)
}
func readLog(ctx context.Context, cmde *exec.Cmd, p *Process, w io.Writer) (done <-chan struct{}, err error) {
cmde.Stdout = w
cmde.Stderr = w
tp := &Process{
ID: sysID("log-access"),
exec: cmde,
}
exited := make(chan struct{}, 2)
p.onExit(func(p *Process) {
exited <- struct{}{}
})
tp.onExit(func(p *Process) {
exited <- struct{}{}
})
if err := pool.add(ctx, tp); err != nil {
return nil, errors.Annotate(err, "Failed to start a tail process")
}
return exited, nil
}
func CommandsList() ([]commands.Command, error) {
var buf bytes.Buffer
cmde := exec.Command("/bin/bash", "-c", config.Get().Commands.ConfigCommand)
cmde.Stdout = &buf
cmde.Stderr = &buf
p := &Process{
ID: sysID("commands-list"),
exec: cmde,
}
exited := make(chan struct{})
p.onExit(func(p *Process) {
close(exited)
})
if err := pool.add(context.Background(), p); err != nil {
return nil, errors.Annotate(err, "Failed to import commands")
}
<-exited
body := buf.Bytes()
var list []commands.Command
if err := json.Unmarshal(body, &list); err != nil {
log.WithFields(log.F{
"error": err,
"json": string(body),
}).Error("Invalid commands JSON")
return nil, errors.Annotate(err, "Failed to decode commands JSON")
}
return list, nil
}
func sysID(name string) string {
return fmt.Sprintf("%s-%d", name, time.Now().UnixNano())
}