diff --git a/OliveTin.proto b/OliveTin.proto index dd7112a..9931729 100644 --- a/OliveTin.proto +++ b/OliveTin.proto @@ -70,6 +70,7 @@ message LogEntry { string user = 7; string userClass = 8; string actionIcon = 9; + repeated string tags = 10; } message GetLogsResponse { diff --git a/cmd/OliveTin/main.go b/cmd/OliveTin/main.go index 11d6422..3c5ada5 100644 --- a/cmd/OliveTin/main.go +++ b/cmd/OliveTin/main.go @@ -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) } diff --git a/internal/config/config.go b/internal/config/config.go index b154d9b..6805136 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -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. diff --git a/internal/executor/executor.go b/internal/executor/executor.go index a233a11..45ebb38 100644 --- a/internal/executor/executor.go +++ b/internal/executor/executor.go @@ -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 } diff --git a/internal/grpcapi/grpcApi.go b/internal/grpcapi/grpcApi.go index c089779..2417ab8 100644 --- a/internal/grpcapi/grpcApi.go +++ b/internal/grpcapi/grpcApi.go @@ -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 } diff --git a/internal/grpcapi/grpcApi_test.go b/internal/grpcapi/grpcApi_test.go index 2a32f9e..9c95e72 100644 --- a/internal/grpcapi/grpcApi_test.go +++ b/internal/grpcapi/grpcApi_test.go @@ -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 { diff --git a/internal/onstartup/startup.go b/internal/onstartup/startup.go new file mode 100644 index 0000000..c08f791 --- /dev/null +++ b/internal/onstartup/startup.go @@ -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) + } + } +} diff --git a/webui/index.html b/webui/index.html index 9e2fdf2..12343ad 100644 --- a/webui/index.html +++ b/webui/index.html @@ -102,7 +102,7 @@ ? - +
stdout
@@ -117,6 +117,7 @@
 						
+
? diff --git a/webui/js/marshaller.js b/webui/js/marshaller.js index 0c327c7..d0b3697 100644 --- a/webui/js/marshaller.js +++ b/webui/js/marshaller.js @@ -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) } } diff --git a/webui/style.css b/webui/style.css index 615a60c..bfb5425 100644 --- a/webui/style.css +++ b/webui/style.css @@ -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;