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:
James Read
2023-02-02 09:35:18 +00:00
committed by GitHub
parent b6196a4b3f
commit f05de1c726
10 changed files with 82 additions and 17 deletions

View File

@@ -70,6 +70,7 @@ message LogEntry {
string user = 7;
string userClass = 8;
string actionIcon = 9;
repeated string tags = 10;
}
message GetLogsResponse {

View File

@@ -5,7 +5,9 @@ import (
log "github.com/sirupsen/logrus"
"github.com/OliveTin/OliveTin/internal/executor"
grpcapi "github.com/OliveTin/OliveTin/internal/grpcapi"
"github.com/OliveTin/OliveTin/internal/onstartup"
updatecheck "github.com/OliveTin/OliveTin/internal/updatecheck"
"github.com/OliveTin/OliveTin/internal/httpservers"
@@ -100,9 +102,13 @@ func main() {
log.Debugf("Config: %+v", cfg)
executor := executor.DefaultExecutor()
go onstartup.Execute(cfg, executor)
go updatecheck.StartUpdateChecker(version, commit, cfg, configDir)
go grpcapi.Start(cfg)
go grpcapi.Start(cfg, executor)
httpservers.StartServers(cfg)
}

View File

@@ -3,14 +3,15 @@ package config
// Action represents the core functionality of OliveTin - commands that show up
// as buttons in the UI.
type Action struct {
ID string
Title string
Icon string
Shell string
CSS map[string]string `mapstructure:"omitempty"`
Timeout int
Acls []string
Arguments []ActionArgument
ID string
Title string
Icon string
Shell string
CSS map[string]string `mapstructure:"omitempty"`
Timeout int
Acls []string
ExecOnStartup bool
Arguments []ActionArgument
}
// ActionArgument objects appear on Actions.

View File

@@ -17,6 +17,7 @@ import (
type ExecutionRequest struct {
ActionName string
Arguments map[string]string
Tags []string
action *config.Action
Cfg *config.Config
AuthenticatedUser *acl.AuthenticatedUser
@@ -33,6 +34,7 @@ type InternalLogEntry struct {
Stderr string
TimedOut bool
ExitCode int32
Tags []string
/*
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.Tags = req.Tags
return true
}

View File

@@ -15,11 +15,12 @@ import (
var (
cfg *config.Config
ex = executor.DefaultExecutor()
)
type oliveTinAPI struct {
pb.UnimplementedOliveTinApiServer
executor *executor.Executor
}
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,
}
return ex.ExecRequest(&execReq), nil
return api.executor.ExecRequest(&execReq), nil
}
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.
for _, logEntry := range ex.Logs {
for _, logEntry := range api.executor.Logs {
ret.Logs = append(ret.Logs, &pb.LogEntry{
ActionTitle: logEntry.ActionTitle,
ActionIcon: logEntry.ActionIcon,
@@ -69,6 +70,7 @@ func (api *oliveTinAPI) GetLogs(ctx ctx.Context, req *pb.GetLogsRequest) (*pb.Ge
Stderr: logEntry.Stderr,
TimedOut: logEntry.TimedOut,
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.
func Start(globalConfig *config.Config) {
func Start(globalConfig *config.Config, ex *executor.Executor) {
cfg = globalConfig
lis, err := net.Listen("tcp", cfg.ListenAddressGrpcActions)
@@ -117,7 +119,7 @@ func Start(globalConfig *config.Config) {
}
grpcServer := grpc.NewServer()
pb.RegisterOliveTinApiServer(grpcServer, newServer())
pb.RegisterOliveTinApiServer(grpcServer, newServer(ex))
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.executor = ex
return &server
}

View File

@@ -14,6 +14,7 @@ import (
pb "github.com/OliveTin/OliveTin/gen/grpc"
config "github.com/OliveTin/OliveTin/internal/config"
"github.com/OliveTin/OliveTin/internal/executor"
)
const bufSize = 1024 * 1024
@@ -21,9 +22,11 @@ const bufSize = 1024 * 1024
var lis *bufconn.Listener
func init() {
ex := executor.DefaultExecutor()
lis = bufconn.Listen(bufSize)
s := grpc.NewServer()
pb.RegisterOliveTinApiServer(s, newServer())
pb.RegisterOliveTinApiServer(s, newServer(ex))
go func() {
if err := s.Serve(lis); err != nil {

View File

@@ -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)
}
}
}

View File

@@ -102,7 +102,7 @@
<td>
<span class = "icon" role = "icon"></span>
<span class = "content">?</span>
<details>
<summary>stdout</summary>
<pre class = "stdout">
@@ -117,6 +117,7 @@
</pre>
</details>
<div class = "tags"></div>
</td>
<td class = "exit-code">?</td>
</tr>

View File

@@ -57,6 +57,14 @@ export function marshalLogsJsonToHtml (json) {
row.querySelector('pre.stderr').innerText = logEntry.stderr
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)
}
}

View File

@@ -387,6 +387,12 @@ input.invalid {
border-radius: 1em;
}
span.tag {
background-color: lightgray;
border-radius: 0.6em;
padding: 0.2em;
}
@media (prefers-color-scheme: dark) {
body {
background-color: #333;