feature: Execute actions on startup. Externalize the executor and add… (#101)
* feature: Execute actions on startup. Externalize the executor and add tags to logs. * fmt: Codestyle fixes
This commit is contained in:
parent
b6196a4b3f
commit
f05de1c726
|
|
@ -70,6 +70,7 @@ message LogEntry {
|
||||||
string user = 7;
|
string user = 7;
|
||||||
string userClass = 8;
|
string userClass = 8;
|
||||||
string actionIcon = 9;
|
string actionIcon = 9;
|
||||||
|
repeated string tags = 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
message GetLogsResponse {
|
message GetLogsResponse {
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,9 @@ import (
|
||||||
|
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
|
"github.com/OliveTin/OliveTin/internal/executor"
|
||||||
grpcapi "github.com/OliveTin/OliveTin/internal/grpcapi"
|
grpcapi "github.com/OliveTin/OliveTin/internal/grpcapi"
|
||||||
|
"github.com/OliveTin/OliveTin/internal/onstartup"
|
||||||
updatecheck "github.com/OliveTin/OliveTin/internal/updatecheck"
|
updatecheck "github.com/OliveTin/OliveTin/internal/updatecheck"
|
||||||
|
|
||||||
"github.com/OliveTin/OliveTin/internal/httpservers"
|
"github.com/OliveTin/OliveTin/internal/httpservers"
|
||||||
|
|
@ -100,9 +102,13 @@ func main() {
|
||||||
|
|
||||||
log.Debugf("Config: %+v", cfg)
|
log.Debugf("Config: %+v", cfg)
|
||||||
|
|
||||||
|
executor := executor.DefaultExecutor()
|
||||||
|
|
||||||
|
go onstartup.Execute(cfg, executor)
|
||||||
|
|
||||||
go updatecheck.StartUpdateChecker(version, commit, cfg, configDir)
|
go updatecheck.StartUpdateChecker(version, commit, cfg, configDir)
|
||||||
|
|
||||||
go grpcapi.Start(cfg)
|
go grpcapi.Start(cfg, executor)
|
||||||
|
|
||||||
httpservers.StartServers(cfg)
|
httpservers.StartServers(cfg)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,14 +3,15 @@ package config
|
||||||
// Action represents the core functionality of OliveTin - commands that show up
|
// Action represents the core functionality of OliveTin - commands that show up
|
||||||
// as buttons in the UI.
|
// as buttons in the UI.
|
||||||
type Action struct {
|
type Action struct {
|
||||||
ID string
|
ID string
|
||||||
Title string
|
Title string
|
||||||
Icon string
|
Icon string
|
||||||
Shell string
|
Shell string
|
||||||
CSS map[string]string `mapstructure:"omitempty"`
|
CSS map[string]string `mapstructure:"omitempty"`
|
||||||
Timeout int
|
Timeout int
|
||||||
Acls []string
|
Acls []string
|
||||||
Arguments []ActionArgument
|
ExecOnStartup bool
|
||||||
|
Arguments []ActionArgument
|
||||||
}
|
}
|
||||||
|
|
||||||
// ActionArgument objects appear on Actions.
|
// ActionArgument objects appear on Actions.
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ import (
|
||||||
type ExecutionRequest struct {
|
type ExecutionRequest struct {
|
||||||
ActionName string
|
ActionName string
|
||||||
Arguments map[string]string
|
Arguments map[string]string
|
||||||
|
Tags []string
|
||||||
action *config.Action
|
action *config.Action
|
||||||
Cfg *config.Config
|
Cfg *config.Config
|
||||||
AuthenticatedUser *acl.AuthenticatedUser
|
AuthenticatedUser *acl.AuthenticatedUser
|
||||||
|
|
@ -33,6 +34,7 @@ type InternalLogEntry struct {
|
||||||
Stderr string
|
Stderr string
|
||||||
TimedOut bool
|
TimedOut bool
|
||||||
ExitCode int32
|
ExitCode int32
|
||||||
|
Tags []string
|
||||||
|
|
||||||
/*
|
/*
|
||||||
The following two properties are obviously on Action normally, but it's useful
|
The following two properties are obviously on Action normally, but it's useful
|
||||||
|
|
@ -185,5 +187,7 @@ func stepExec(req *ExecutionRequest) bool {
|
||||||
req.logEntry.TimedOut = true
|
req.logEntry.TimedOut = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
req.logEntry.Tags = req.Tags
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,11 +15,12 @@ import (
|
||||||
|
|
||||||
var (
|
var (
|
||||||
cfg *config.Config
|
cfg *config.Config
|
||||||
ex = executor.DefaultExecutor()
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type oliveTinAPI struct {
|
type oliveTinAPI struct {
|
||||||
pb.UnimplementedOliveTinApiServer
|
pb.UnimplementedOliveTinApiServer
|
||||||
|
|
||||||
|
executor *executor.Executor
|
||||||
}
|
}
|
||||||
|
|
||||||
func (api *oliveTinAPI) StartAction(ctx ctx.Context, req *pb.StartActionRequest) (*pb.StartActionResponse, error) {
|
func (api *oliveTinAPI) StartAction(ctx ctx.Context, req *pb.StartActionRequest) (*pb.StartActionResponse, error) {
|
||||||
|
|
@ -38,7 +39,7 @@ func (api *oliveTinAPI) StartAction(ctx ctx.Context, req *pb.StartActionRequest)
|
||||||
Cfg: cfg,
|
Cfg: cfg,
|
||||||
}
|
}
|
||||||
|
|
||||||
return ex.ExecRequest(&execReq), nil
|
return api.executor.ExecRequest(&execReq), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (api *oliveTinAPI) GetDashboardComponents(ctx ctx.Context, req *pb.GetDashboardComponentsRequest) (*pb.GetDashboardComponentsResponse, error) {
|
func (api *oliveTinAPI) GetDashboardComponents(ctx ctx.Context, req *pb.GetDashboardComponentsRequest) (*pb.GetDashboardComponentsResponse, error) {
|
||||||
|
|
@ -60,7 +61,7 @@ func (api *oliveTinAPI) GetLogs(ctx ctx.Context, req *pb.GetLogsRequest) (*pb.Ge
|
||||||
|
|
||||||
// TODO Limit to 10 entries or something to prevent browser lag.
|
// TODO Limit to 10 entries or something to prevent browser lag.
|
||||||
|
|
||||||
for _, logEntry := range ex.Logs {
|
for _, logEntry := range api.executor.Logs {
|
||||||
ret.Logs = append(ret.Logs, &pb.LogEntry{
|
ret.Logs = append(ret.Logs, &pb.LogEntry{
|
||||||
ActionTitle: logEntry.ActionTitle,
|
ActionTitle: logEntry.ActionTitle,
|
||||||
ActionIcon: logEntry.ActionIcon,
|
ActionIcon: logEntry.ActionIcon,
|
||||||
|
|
@ -69,6 +70,7 @@ func (api *oliveTinAPI) GetLogs(ctx ctx.Context, req *pb.GetLogsRequest) (*pb.Ge
|
||||||
Stderr: logEntry.Stderr,
|
Stderr: logEntry.Stderr,
|
||||||
TimedOut: logEntry.TimedOut,
|
TimedOut: logEntry.TimedOut,
|
||||||
ExitCode: logEntry.ExitCode,
|
ExitCode: logEntry.ExitCode,
|
||||||
|
Tags: logEntry.Tags,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -107,7 +109,7 @@ func (api *oliveTinAPI) WhoAmI(ctx ctx.Context, req *pb.WhoAmIRequest) (*pb.WhoA
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start will start the GRPC API.
|
// Start will start the GRPC API.
|
||||||
func Start(globalConfig *config.Config) {
|
func Start(globalConfig *config.Config, ex *executor.Executor) {
|
||||||
cfg = globalConfig
|
cfg = globalConfig
|
||||||
|
|
||||||
lis, err := net.Listen("tcp", cfg.ListenAddressGrpcActions)
|
lis, err := net.Listen("tcp", cfg.ListenAddressGrpcActions)
|
||||||
|
|
@ -117,7 +119,7 @@ func Start(globalConfig *config.Config) {
|
||||||
}
|
}
|
||||||
|
|
||||||
grpcServer := grpc.NewServer()
|
grpcServer := grpc.NewServer()
|
||||||
pb.RegisterOliveTinApiServer(grpcServer, newServer())
|
pb.RegisterOliveTinApiServer(grpcServer, newServer(ex))
|
||||||
|
|
||||||
err = grpcServer.Serve(lis)
|
err = grpcServer.Serve(lis)
|
||||||
|
|
||||||
|
|
@ -126,7 +128,8 @@ func Start(globalConfig *config.Config) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func newServer() *oliveTinAPI {
|
func newServer(ex *executor.Executor) *oliveTinAPI {
|
||||||
server := oliveTinAPI{}
|
server := oliveTinAPI{}
|
||||||
|
server.executor = ex
|
||||||
return &server
|
return &server
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ import (
|
||||||
|
|
||||||
pb "github.com/OliveTin/OliveTin/gen/grpc"
|
pb "github.com/OliveTin/OliveTin/gen/grpc"
|
||||||
config "github.com/OliveTin/OliveTin/internal/config"
|
config "github.com/OliveTin/OliveTin/internal/config"
|
||||||
|
"github.com/OliveTin/OliveTin/internal/executor"
|
||||||
)
|
)
|
||||||
|
|
||||||
const bufSize = 1024 * 1024
|
const bufSize = 1024 * 1024
|
||||||
|
|
@ -21,9 +22,11 @@ const bufSize = 1024 * 1024
|
||||||
var lis *bufconn.Listener
|
var lis *bufconn.Listener
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
ex := executor.DefaultExecutor()
|
||||||
|
|
||||||
lis = bufconn.Listen(bufSize)
|
lis = bufconn.Listen(bufSize)
|
||||||
s := grpc.NewServer()
|
s := grpc.NewServer()
|
||||||
pb.RegisterOliveTinApiServer(s, newServer())
|
pb.RegisterOliveTinApiServer(s, newServer(ex))
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
if err := s.Serve(lis); err != nil {
|
if err := s.Serve(lis); err != nil {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
package onstartup
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/OliveTin/OliveTin/internal/acl"
|
||||||
|
config "github.com/OliveTin/OliveTin/internal/config"
|
||||||
|
"github.com/OliveTin/OliveTin/internal/executor"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Execute(cfg *config.Config, ex *executor.Executor) {
|
||||||
|
user := &acl.AuthenticatedUser{
|
||||||
|
Username: "startup-user",
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, action := range cfg.Actions {
|
||||||
|
if action.ExecOnStartup {
|
||||||
|
log.WithFields(log.Fields{
|
||||||
|
"action": action.Title,
|
||||||
|
}).Infof("Startup action")
|
||||||
|
|
||||||
|
req := &executor.ExecutionRequest{
|
||||||
|
ActionName: action.Title,
|
||||||
|
Arguments: nil,
|
||||||
|
Cfg: cfg,
|
||||||
|
Tags: []string{"startup"},
|
||||||
|
AuthenticatedUser: user,
|
||||||
|
}
|
||||||
|
|
||||||
|
ex.ExecRequest(req)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -102,7 +102,7 @@
|
||||||
<td>
|
<td>
|
||||||
<span class = "icon" role = "icon"></span>
|
<span class = "icon" role = "icon"></span>
|
||||||
<span class = "content">?</span>
|
<span class = "content">?</span>
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary>stdout</summary>
|
<summary>stdout</summary>
|
||||||
<pre class = "stdout">
|
<pre class = "stdout">
|
||||||
|
|
@ -117,6 +117,7 @@
|
||||||
</pre>
|
</pre>
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
|
<div class = "tags"></div>
|
||||||
</td>
|
</td>
|
||||||
<td class = "exit-code">?</td>
|
<td class = "exit-code">?</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
|
||||||
|
|
@ -57,6 +57,14 @@ export function marshalLogsJsonToHtml (json) {
|
||||||
row.querySelector('pre.stderr').innerText = logEntry.stderr
|
row.querySelector('pre.stderr').innerText = logEntry.stderr
|
||||||
row.querySelector('.exit-code').innerText = logTableExitCode
|
row.querySelector('.exit-code').innerText = logTableExitCode
|
||||||
|
|
||||||
|
for (const tag of logEntry.tags) {
|
||||||
|
const domTag = document.createElement('span')
|
||||||
|
domTag.classList.add('tag')
|
||||||
|
domTag.innerText = tag
|
||||||
|
|
||||||
|
row.querySelector('.tags').append(domTag)
|
||||||
|
}
|
||||||
|
|
||||||
document.querySelector('#logTableBody').prepend(row)
|
document.querySelector('#logTableBody').prepend(row)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -387,6 +387,12 @@ input.invalid {
|
||||||
border-radius: 1em;
|
border-radius: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
span.tag {
|
||||||
|
background-color: lightgray;
|
||||||
|
border-radius: 0.6em;
|
||||||
|
padding: 0.2em;
|
||||||
|
}
|
||||||
|
|
||||||
@media (prefers-color-scheme: dark) {
|
@media (prefers-color-scheme: dark) {
|
||||||
body {
|
body {
|
||||||
background-color: #333;
|
background-color: #333;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue