diff --git a/service/internal/entityfiles/entityfiles.go b/service/internal/entityfiles/entityfiles.go index fecc381..1b06b86 100644 --- a/service/internal/entityfiles/entityfiles.go +++ b/service/internal/entityfiles/entityfiles.go @@ -32,7 +32,7 @@ func SetupEntityFileWatchers(cfg *config.Config) { configDir = configDirVar } - for entityIndex, _ := range cfg.Entities { // #337 - iterate by key, not by value + for entityIndex := range cfg.Entities { // #337 - iterate by key, not by value ef := cfg.Entities[entityIndex] p := ef.File @@ -73,12 +73,12 @@ func loadEntityFileJson(filename string, entityname string) { return } - data := make([]map[string]interface{}, 0) + data := make([]map[string]any, 0) decoder := json.NewDecoder(bytes.NewReader(jfile)) for decoder.More() { - d := make(map[string]interface{}) + d := make(map[string]any) err := decoder.Decode(&d) @@ -106,7 +106,7 @@ func loadEntityFileYaml(filename string, entityname string) { return } - data := make([]map[string]interface{}, 1) + data := make([]map[string]any, 1) err = yaml.Unmarshal(yfile, &data) @@ -117,7 +117,7 @@ func loadEntityFileYaml(filename string, entityname string) { updateSvFromFile(entityname, data) } -func updateSvFromFile(entityname string, data []map[string]interface{}) { +func updateSvFromFile(entityname string, data []map[string]any) { log.Debugf("updateSvFromFile: %+v", data) count := len(data) @@ -137,23 +137,23 @@ func updateSvFromFile(entityname string, data []map[string]interface{}) { } } -func serializeValueToSv(prefix string, value interface{}) { - if m, ok := value.(map[string]interface{}); ok { // if value is a map we need to flatten it +func serializeValueToSv(prefix string, value any) { + if m, ok := value.(map[string]any); ok { // if value is a map we need to flatten it serializeMapToSv(prefix, m) - } else if s, ok := value.([]interface{}); ok { // if value is a slice we need to flatten it + } else if s, ok := value.([]any); ok { // if value is a slice we need to flatten it serializeSliceToSv(prefix, s) } else { sv.Set(prefix, fmt.Sprintf("%v", value)) } } -func serializeMapToSv(prefix string, m map[string]interface{}) { +func serializeMapToSv(prefix string, m map[string]any) { for k, v := range m { serializeValueToSv(prefix+"."+k, v) } } -func serializeSliceToSv(prefix string, s []interface{}) { +func serializeSliceToSv(prefix string, s []any) { sv.Set(prefix+".count", fmt.Sprintf("%v", len(s))) for i, v := range s { diff --git a/service/internal/executor/arguments.go b/service/internal/executor/arguments.go index 0276d5f..cd12ce3 100644 --- a/service/internal/executor/arguments.go +++ b/service/internal/executor/arguments.go @@ -5,7 +5,6 @@ import ( sv "github.com/OliveTin/OliveTin/internal/stringvariables" log "github.com/sirupsen/logrus" - "errors" "fmt" "net/mail" "net/url" @@ -34,7 +33,7 @@ func parseCommandForReplacements(shellCommand string, values map[string]string) argValue, argProvided := values[argName] if !argProvided { - return "", errors.New("Required arg not provided: " + argName) + return "", fmt.Errorf("required arg not provided: %v", argName) } shellCommand = strings.ReplaceAll(shellCommand, match[0], argValue) @@ -107,7 +106,7 @@ func typecheckActionArgument(name string, value string, action *config.Action) e arg := action.FindArg(name) if arg == nil { - return errors.New("Action arg not defined: " + name) + return fmt.Errorf("action arg not defined: %v", name) } if value == "" { @@ -145,7 +144,7 @@ func TypeSafetyCheck(name string, value string, argumentType string) error { func typecheckNull(arg *config.ActionArgument) error { if arg.RejectNull { - return errors.New("Null values are not allowed") + return fmt.Errorf("null values are not allowed") } return nil @@ -162,7 +161,7 @@ func typecheckChoice(value string, arg *config.ActionArgument) error { } } - return errors.New("argument value is not one of the predefined choices") + return fmt.Errorf("argument value is not one of the predefined choices") } func typecheckChoiceEntity(value string, arg *config.ActionArgument) error { @@ -176,7 +175,7 @@ func typecheckChoiceEntity(value string, arg *config.ActionArgument) error { } } - return errors.New("argument value cannot be found in entities") + return fmt.Errorf("argument value cannot be found in entities") } func typeSafetyCheckEmail(value string) error { @@ -211,7 +210,7 @@ func typeSafetyCheckRegex(name string, value string, argumentType string) error pattern, found = typecheckRegex[argumentType] if !found { - return errors.New("argument type not implemented " + argumentType) + return fmt.Errorf("argument type not implemented %v", argumentType) } } @@ -225,7 +224,7 @@ func typeSafetyCheckRegex(name string, value string, argumentType string) error "pattern": pattern, }).Warn("Arg type check safety failure") - return errors.New(fmt.Sprintf("invalid argument %v, doesn't match %v", name, argumentType)) + return fmt.Errorf("invalid argument %v, doesn't match %v", name, argumentType) } return nil diff --git a/service/internal/executor/executor.go b/service/internal/executor/executor.go index 309dfa1..4ae17f3 100644 --- a/service/internal/executor/executor.go +++ b/service/internal/executor/executor.go @@ -310,9 +310,9 @@ func stepConcurrencyCheck(req *ExecutionRequest) bool { // Note that the current execution is counted int the logs, so when checking we +1 if concurrentCount >= (req.Action.MaxConcurrent + 1) { log.WithFields(log.Fields{ - "actionTitle": req.logEntry.ActionTitle, + "actionTitle": req.logEntry.ActionTitle, "concurrentCount": concurrentCount, - "maxConcurrent": req.Action.MaxConcurrent, + "maxConcurrent": req.Action.MaxConcurrent, }).Warnf("Blocked from executing due to concurrency limit") req.logEntry.Output = "Blocked from executing due to concurrency limit" @@ -570,7 +570,14 @@ func stepExec(req *ExecutionRequest) bool { }).Warnf("Action timed out") // The context timeout should kill the process, but let's make sure. - req.executor.Kill(req.logEntry) + err := req.executor.Kill(req.logEntry) + + if err != nil { + log.WithFields(log.Fields{ + "actionTitle": req.logEntry.ActionTitle, + }).Warnf("could not kill process: %v", err) + } + req.logEntry.TimedOut = true req.logEntry.Output += "OliveTin::timeout - this action timed out after " + fmt.Sprintf("%v", req.Action.Timeout) + " seconds. If you need more time for this action, set a longer timeout. See https://docs.olivetin.app/timeout.html for more help." } diff --git a/service/internal/grpcapi/grpcApi.go b/service/internal/grpcapi/grpcApi.go index daa5cbe..6bc85ad 100644 --- a/service/internal/grpcapi/grpcApi.go +++ b/service/internal/grpcapi/grpcApi.go @@ -12,7 +12,7 @@ import ( "google.golang.org/grpc/metadata" "google.golang.org/grpc/status" - "errors" + "fmt" "net" acl "github.com/OliveTin/OliveTin/internal/acl" @@ -154,7 +154,7 @@ func (api *oliveTinAPI) StartActionAndWait(ctx ctx.Context, req *apiv1.StartActi LogEntry: internalLogEntryToPb(internalLogEntry), }, nil } else { - return nil, errors.New("Execution not found!") + return nil, fmt.Errorf("execution not found") } } diff --git a/service/internal/httpservers/restapi.go b/service/internal/httpservers/restapi.go index 1749a02..3681a59 100644 --- a/service/internal/httpservers/restapi.go +++ b/service/internal/httpservers/restapi.go @@ -5,6 +5,7 @@ import ( "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" log "github.com/sirupsen/logrus" "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" "google.golang.org/grpc/metadata" "google.golang.org/protobuf/encoding/protojson" "google.golang.org/protobuf/reflect/protoreflect" @@ -190,7 +191,11 @@ func newMux() *runtime.ServeMux { ctx := context.Background() - opts := []grpc.DialOption{grpc.WithInsecure()} + opts := []grpc.DialOption{ + grpc.WithTransportCredentials( + insecure.NewCredentials(), + ), + } err := apiv1.RegisterOliveTinApiServiceHandlerFromEndpoint(ctx, mux, cfg.ListenAddressGrpcActions, opts) diff --git a/service/internal/httpservers/restapi_auth_jwt_test.go b/service/internal/httpservers/restapi_auth_jwt_test.go index 921daf0..8778aed 100644 --- a/service/internal/httpservers/restapi_auth_jwt_test.go +++ b/service/internal/httpservers/restapi_auth_jwt_test.go @@ -93,7 +93,11 @@ func testJwkValidation(t *testing.T, expire int64, expectCode int) { fmt.Println(string(body)) } - srv.Shutdown(context.TODO()) + err = srv.Shutdown(context.TODO()) + + if err != nil { + t.Fatalf("Server shutdown error: %+v", err) + } } func TestJWTSignatureVerificationSucceeds(t *testing.T) { @@ -114,7 +118,7 @@ func TestJWTHeader(t *testing.T) { cfg.AuthJwtClaimUsername = "sub" cfg.AuthJwtClaimUserGroup = "olivetinGroup" cfg.AuthJwtHeader = "Authorization" - SetGlobalRestConfig(cfg) // ugly, setting global var, we should pass configs as params to modules... :/ + SetGlobalRestConfig(cfg) // Ugly, setting global var, we should pass configs as params to modules... :/ token := jwt.New(jwt.SigningMethodRS256) diff --git a/service/internal/httpservers/restapi_auth_oauth2.go b/service/internal/httpservers/restapi_auth_oauth2.go index bbc8231..9bff1a4 100644 --- a/service/internal/httpservers/restapi_auth_oauth2.go +++ b/service/internal/httpservers/restapi_auth_oauth2.go @@ -76,11 +76,11 @@ func completeProviderConfig(providerName string, providerConfig *config.OAuth2Pr } } -func getOAuth2Config(cfg *config.Config, providerName string) (*oauth2.Config, error) { +func getOAuth2Config(providerName string) (*oauth2.Config, error) { config, ok := registeredProviders[providerName] if !ok { - return nil, fmt.Errorf("Provider not found in config: %v", providerName) + return nil, fmt.Errorf("provider not found in config: %v", providerName) } return config, nil @@ -118,7 +118,7 @@ func handleOAuthLogin(w http.ResponseWriter, r *http.Request) { } providerName := r.URL.Query().Get("provider") - provider, err := getOAuth2Config(cfg, providerName) + provider, err := getOAuth2Config(providerName) if err != nil { log.Errorf("Failed to get provider config: %v %v", providerName, err) @@ -260,7 +260,7 @@ func handleOAuthCallback(w http.ResponseWriter, r *http.Request) { } log.WithFields(log.Fields{ - "state": state, + "state": state, "username": registeredStates[state].Username, }).Info("OAuth2 login successful") @@ -273,11 +273,17 @@ type UserInfo struct { Usergroup string } +//gocyclo:ignore func getUserInfo(client *http.Client, provider *config.OAuth2Provider) *UserInfo { ret := &UserInfo{} res, err := client.Get(provider.WhoamiUrl) + if err != nil { + log.Errorf("Failed to get user data: %v", err) + return ret + } + if res.StatusCode != http.StatusOK { log.Errorf("Failed to get user data: %v", res.StatusCode) return ret @@ -287,7 +293,12 @@ func getUserInfo(client *http.Client, provider *config.OAuth2Provider) *UserInfo contents, err := io.ReadAll(res.Body) - var userData map[string]interface{} + if err != nil { + log.Errorf("Failed to read user data: %v", err) + return ret + } + + var userData map[string]any err = json.Unmarshal([]byte(contents), &userData) @@ -303,7 +314,7 @@ func getUserInfo(client *http.Client, provider *config.OAuth2Provider) *UserInfo return ret } -func getDataField(data map[string]interface{}, field string) string { +func getDataField(data map[string]any, field string) string { if field == "" { return "" } diff --git a/service/internal/httpservers/webuiServer.go b/service/internal/httpservers/webuiServer.go index 3beb423..b1dd19d 100644 --- a/service/internal/httpservers/webuiServer.go +++ b/service/internal/httpservers/webuiServer.go @@ -94,7 +94,7 @@ func generateThemeCss(w http.ResponseWriter, r *http.Request) { customThemeCssRead = true if _, err := os.Stat(themeCssFilename); err == nil { - customThemeCss, err = os.ReadFile(themeCssFilename) + customThemeCss, _ = os.ReadFile(themeCssFilename) } else { log.Debugf("Theme CSS not read: %v", err) customThemeCss = []byte("/* not found */") diff --git a/service/internal/oncalendarfile/calendar.go b/service/internal/oncalendarfile/calendar.go index 97658b1..6e7bdb8 100644 --- a/service/internal/oncalendarfile/calendar.go +++ b/service/internal/oncalendarfile/calendar.go @@ -12,11 +12,7 @@ import ( "time" ) -var calendar map[*config.Action][]*time.Timer - func Schedule(cfg *config.Config, ex *executor.Executor) { - calendar = make(map[*config.Action][]*time.Timer) - for _, action := range cfg.Actions { if action.ExecOnCalendarFile != "" { x := func(filename string) { @@ -100,7 +96,6 @@ func sleepUntil(ctx context.Context, instant time.Time, action *config.Action, c } func exec(instant time.Time, action *config.Action, cfg *config.Config, ex *executor.Executor) { - // calendar[action] = append(calendar[action], timer) log.WithFields(log.Fields{ "instant": instant, "actionTitle": action.Title, diff --git a/service/internal/stringvariables/map.go b/service/internal/stringvariables/map.go index 57e79e1..23f32ec 100644 --- a/service/internal/stringvariables/map.go +++ b/service/internal/stringvariables/map.go @@ -66,7 +66,7 @@ func Set(key string, value string) { func RemoveKeysThatStartWith(search string) { rwmutex.Lock() - for k, _ := range contents { + for k := range contents { if strings.HasPrefix(k, search) { delete(contents, k) } diff --git a/service/internal/updatecheck/updateCheck.go b/service/internal/updatecheck/updateCheck.go index d46911e..5c35f17 100644 --- a/service/internal/updatecheck/updateCheck.go +++ b/service/internal/updatecheck/updateCheck.go @@ -33,10 +33,15 @@ func StartUpdateChecker(cfg *config.Config) { // 1st: Every 24h - very spammy. // 2nd: Every 7d - (168 hours - much more reasonable, but it checks in at the same time/day each week. // Current: Every 100h is not so spammy, and has the advantage that the checkin time "shifts" hours. - s.AddFunc("@every 100h", func() { + _, err := s.AddFunc("@every 100h", func() { actualCheckForUpdate() }) + if err != nil { + log.Errorf("update check cron job failed to start: %v", err) + return + } + go actualCheckForUpdate() // On startup go s.Start() @@ -48,7 +53,7 @@ func parseVersion(input []byte) string { err := json.Unmarshal(input, &versionMap) if err != nil { - log.Errorf("Update check unmarshal failure: %v", err) + log.Errorf("update check unmarshal failure: %v", err) return "none" } else { log.Infof("Update check remote version: %+v, latest version: %+v", versionMap.Latest, installationinfo.Build.Version) @@ -95,7 +100,12 @@ func doRequest() string { versionMap, _ := io.ReadAll(resp.Body) - defer resp.Body.Close() + err = resp.Body.Close() + + if err != nil { + log.Errorf("Update check failed to close body %v", err) + return "none" + } return parseVersion(versionMap) } diff --git a/service/internal/websocket/websocket.go b/service/internal/websocket/websocket.go index d490da7..2a4834f 100644 --- a/service/internal/websocket/websocket.go +++ b/service/internal/websocket/websocket.go @@ -118,7 +118,11 @@ func broadcast(pbmsg protoreflect.ProtoMessage) { sendmutex.Lock() for _, client := range clients { - client.conn.WriteMessage(ws.TextMessage, hackyMessage) + err := client.conn.WriteMessage(ws.TextMessage, hackyMessage) + + if err != nil { + log.Warnf("websocket send error: %v", err) + } } sendmutex.Unlock() }