diff --git a/service/internal/acl/acl_test.go b/service/internal/acl/acl_test.go index 1ef14b6..2b6bd0e 100644 --- a/service/internal/acl/acl_test.go +++ b/service/internal/acl/acl_test.go @@ -9,10 +9,10 @@ import ( func Test_hasGroupsMatch(t *testing.T) { tests := []struct { name string - aclMatchUsergroups []string usergroupLine string - matches bool sep string + aclMatchUsergroups []string + matches bool }{ { name: "No groups match", diff --git a/service/internal/api/api.go b/service/internal/api/api.go index fa3a8bb..ab9ae00 100644 --- a/service/internal/api/api.go +++ b/service/internal/api/api.go @@ -63,9 +63,9 @@ func (api *oliveTinAPI) copyOfStreamingClients() []*streamingClient { type streamingClient struct { channel chan *apiv1.EventStreamResponse AuthenticatedUser *authpublic.AuthenticatedUser - heartbeatStopOnce sync.Once heartbeatStop chan struct{} heartbeatDone chan struct{} + heartbeatStopOnce sync.Once } func (c *streamingClient) stopHeartbeat() { diff --git a/service/internal/api/apiActions.go b/service/internal/api/apiActions.go index aaac144..cb7bed8 100644 --- a/service/internal/api/apiActions.go +++ b/service/internal/api/apiActions.go @@ -25,9 +25,9 @@ type DashboardRenderRequest struct { AuthenticatedUser *authpublic.AuthenticatedUser cfg *config.Config ex *executor.Executor + activeBindingStates map[string]bindingActiveState EntityType string EntityKey string - activeBindingStates map[string]bindingActiveState } func activeBindingID(entry *executor.InternalLogEntry) string { diff --git a/service/internal/api/api_test.go b/service/internal/api/api_test.go index f1028eb..0ec5a9d 100644 --- a/service/internal/api/api_test.go +++ b/service/internal/api/api_test.go @@ -226,9 +226,9 @@ func validateConsistency(t *testing.T, client apiv1connect.OliveTinApiServiceCli func TestEvaluateEnabledExpression(t *testing.T) { tests := []struct { + entity *entities.Entity name string expression string - entity *entities.Entity expectedResult bool }{ { diff --git a/service/internal/auth/authpublic/authenticateduser.go b/service/internal/auth/authpublic/authenticateduser.go index 077f220..a4e47fe 100644 --- a/service/internal/auth/authpublic/authenticateduser.go +++ b/service/internal/auth/authpublic/authenticateduser.go @@ -10,15 +10,12 @@ import ( // User represents a person. type AuthenticatedUser struct { - Username string - UsergroupLine string - - Provider string - SID string - - Acls []string - EffectivePolicy *config.ConfigurationPolicy + Username string + UsergroupLine string + Provider string + SID string + Acls []string } func (u *AuthenticatedUser) IsGuest() bool { diff --git a/service/internal/auth/authpublic/authenticateduser_test.go b/service/internal/auth/authpublic/authenticateduser_test.go index 42a4726..96bca18 100644 --- a/service/internal/auth/authpublic/authenticateduser_test.go +++ b/service/internal/auth/authpublic/authenticateduser_test.go @@ -10,8 +10,8 @@ func Test_parseUsergroupLine(t *testing.T) { tests := []struct { name string usergroupLine string - expectedGroups []string sep string + expectedGroups []string }{ { name: "Default separator (space)", diff --git a/service/internal/auth/otoauth2/restapi_auth_oauth2.go b/service/internal/auth/otoauth2/restapi_auth_oauth2.go index 5073b7e..ddae1ec 100644 --- a/service/internal/auth/otoauth2/restapi_auth_oauth2.go +++ b/service/internal/auth/otoauth2/restapi_auth_oauth2.go @@ -22,9 +22,9 @@ import ( type OAuth2Handler struct { cfg *config.Config - mu sync.RWMutex registeredStates map[string]*oauth2State registeredProviders map[string]*oauth2.Config + mu sync.RWMutex } func NewOAuth2Handler(cfg *config.Config) *OAuth2Handler { @@ -58,11 +58,11 @@ func NewOAuth2Handler(cfg *config.Config) *OAuth2Handler { } type oauth2State struct { + createdAt time.Time providerConfig *oauth2.Config providerName string Username string Usergroup string - createdAt time.Time } const ( diff --git a/service/internal/config/config.go b/service/internal/config/config.go index 59e6f8e..5106c15 100644 --- a/service/internal/config/config.go +++ b/service/internal/config/config.go @@ -13,34 +13,33 @@ const JustificationRequiredNoTemplate = " " // Action represents the core functionality of OliveTin - commands that show up // as buttons in the UI. type Action struct { - ID string `koanf:"id"` - Title string `koanf:"title"` - Icon string `koanf:"icon"` + SaveLogs SaveLogsConfig `koanf:"saveLogs"` Shell string `koanf:"shell"` - Exec []string `koanf:"exec"` - ShellAfterCompleted string `koanf:"shellAfterCompleted"` - Timeout int `koanf:"timeout"` - Acls []string `koanf:"acls"` - Entity string `koanf:"entity"` - Hidden bool `koanf:"hidden"` - ExecOnStartup bool `koanf:"execOnStartup"` - ExecOnCron []string `koanf:"execOnCron"` - ExecOnFileCreatedInDir []string `koanf:"execOnFileCreatedInDir"` - ExecOnFileChangedInDir []string `koanf:"execOnFileChangedInDir"` + Icon string `koanf:"icon"` ExecOnCalendarFile string `koanf:"execOnCalendarFile"` + SourceFile string `koanf:"-"` + ShellAfterCompleted string `koanf:"shellAfterCompleted"` + Justification string `koanf:"justification"` + EnabledExpression string `koanf:"enabledExpression"` + Entity string `koanf:"entity"` + Title string `koanf:"title"` + PopupOnStart string `koanf:"popupOnStart"` + OnClick string `koanf:"onclick"` + ID string `koanf:"id"` + MaxRate []RateSpec `koanf:"maxRate"` + Acls []string `koanf:"acls"` ExecOnWebhook []WebhookConfig `koanf:"execOnWebhook"` Triggers []string `koanf:"triggers"` - MaxConcurrent int `koanf:"maxConcurrent"` - MaxRate []RateSpec `koanf:"maxRate"` + Exec []string `koanf:"exec"` + ExecOnFileCreatedInDir []string `koanf:"execOnFileCreatedInDir"` Arguments []ActionArgument `koanf:"arguments"` - OnClick string `koanf:"onclick"` - PopupOnStart string `koanf:"popupOnStart"` - SaveLogs SaveLogsConfig `koanf:"saveLogs"` - EnabledExpression string `koanf:"enabledExpression"` + ExecOnCron []string `koanf:"execOnCron"` Groups []string `koanf:"groups"` - Justification string `koanf:"justification"` - // SourceFile is set by OliveTin when loading config (not user YAML). - SourceFile string `koanf:"-"` + ExecOnFileChangedInDir []string `koanf:"execOnFileChangedInDir"` + Timeout int `koanf:"timeout"` + MaxConcurrent int `koanf:"maxConcurrent"` + Hidden bool `koanf:"hidden"` + ExecOnStartup bool `koanf:"execOnStartup"` } func (action *Action) RequiresJustification() bool { @@ -61,23 +60,23 @@ func (action *Action) JustificationTemplateText() string { // ActionGroup defines shared limits and metadata for a set of actions. type ActionGroup struct { + Icon string `koanf:"icon"` MaxConcurrent int `koanf:"maxConcurrent"` QueueSize int `koanf:"queueSize"` - Icon string `koanf:"icon"` } // ActionArgument objects appear on Actions. type ActionArgument struct { + Suggestions map[string]string `koanf:"suggestions"` Name string `koanf:"name"` Title string `koanf:"title"` Description string `koanf:"description"` Type string `koanf:"type"` Default string `koanf:"default"` - Choices []ActionArgumentChoice `koanf:"choices"` Entity string `koanf:"entity"` - RejectNull bool `koanf:"rejectNull"` - Suggestions map[string]string `koanf:"suggestions"` SuggestionsBrowserKey string `koanf:"suggestionsBrowserKey"` + Choices []ActionArgumentChoice `koanf:"choices"` + RejectNull bool `koanf:"rejectNull"` } // ActionArgumentChoice represents a predefined choice for an argument. @@ -88,8 +87,8 @@ type ActionArgumentChoice struct { // RateSpec allows you to set a max frequency for an action. type RateSpec struct { - Limit int `koanf:"limit"` Duration string `koanf:"duration"` + Limit int `koanf:"limit"` } // WebhookConfig defines configuration for generic webhook triggers. @@ -111,9 +110,8 @@ type EntityFile struct { File string `koanf:"file"` Name string `koanf:"name"` Icon string `koanf:"icon"` + SourceFile string `koanf:"-"` Properties []EntityProperty `koanf:"properties"` - // SourceFile is set by OliveTin when loading config (not user YAML). - SourceFile string `koanf:"-"` } // EntityProperty defines a column shown when listing entity instances in the UI. @@ -133,11 +131,11 @@ type PermissionsList struct { // AccessControlList defines what permissions apply to a user or user group. type AccessControlList struct { Name string `koanf:"name"` - AddToEveryAction bool `koanf:"addToEveryAction"` MatchUsergroups []string `koanf:"matchUsergroups"` MatchUsernames []string `koanf:"matchUsernames"` Permissions PermissionsList `koanf:"permissions"` Policy ConfigurationPolicy `koanf:"policy"` + AddToEveryAction bool `koanf:"addToEveryAction"` } // ConfigurationPolicy defines global settings which are overridden with an ACL. @@ -154,88 +152,87 @@ type PrometheusConfig struct { // SecurityConfig allows users to fine tune the security related HTTP headers and cookie options. type SecurityConfig struct { - HeaderContentSecurityPolicy bool `koanf:"headerContentSecurityPolicy"` ContentSecurityPolicy string `koanf:"contentSecurityPolicy"` + XFrameOptions string `koanf:"xFrameOptions"` + HeaderContentSecurityPolicy bool `koanf:"headerContentSecurityPolicy"` HeaderXContentTypeOptions bool `koanf:"headerXContentTypeOptions"` HeaderXFrameOptions bool `koanf:"headerXFrameOptions"` - XFrameOptions string `koanf:"xFrameOptions"` ForceSecureCookies bool `koanf:"forceSecureCookies"` } // Config is the global config used through the whole app. type Config struct { - UseSingleHTTPFrontend bool `koanf:"useSingleHTTPFrontend"` - ThemeName string `koanf:"themeName"` - ThemeCacheDisabled bool `koanf:"themeCacheDisabled"` - ListenAddressSingleHTTPFrontend string `koanf:"listenAddressSingleHTTPFrontend"` - ListenAddressWebUI string `koanf:"listenAddressWebUI"` + ActionGroups map[string]*ActionGroup `koanf:"actionGroups"` + AuthOAuth2Providers map[string]*OAuth2Provider `koanf:"authOAuth2Providers"` + SaveLogs SaveLogsConfig `koanf:"saveLogs"` + DefaultIconForBack string `koanf:"defaultIconForBack"` + AuthOAuth2RedirectURL string `koanf:"authOAuth2RedirectUrl"` ListenAddressRestActions string `koanf:"listenAddressRestActions"` ListenAddressPrometheus string `koanf:"listenAddressPrometheus"` ExternalRestAddress string `koanf:"externalRestAddress"` LogLevel string `koanf:"logLevel"` - LogDebugOptions LogDebugOptions `koanf:"logDebugOptions"` - LogHistoryPageSize int64 `koanf:"logHistoryPageSize"` - ActionGroups map[string]*ActionGroup `koanf:"actionGroups"` - Actions []*Action `koanf:"actions"` - Entities []*EntityFile `koanf:"entities"` - Dashboards []*DashboardComponent `koanf:"dashboards"` - CheckForUpdates bool `koanf:"checkForUpdates"` + ThemeName string `koanf:"themeName"` + ServiceLogs ServiceLogsConfig `koanf:"serviceLogs"` + ListenAddressSingleHTTPFrontend string `koanf:"listenAddressSingleHTTPFrontend"` + AuthJwtHmacSecret string `koanf:"authJwtHmacSecret"` + AuthJwtCertsURL string `koanf:"authJwtCertsUrl"` + DefaultIconForActions string `koanf:"defaultIconForActions"` + Include string `koanf:"include"` PageTitle string `koanf:"pageTitle"` - ShowFooter bool `koanf:"showFooter"` - ShowNavigation bool `koanf:"showNavigation"` - ShowNewVersions bool `koanf:"showNewVersions"` - ShowNavigateOnStartIcons bool `koanf:"showNavigateOnStartIcons"` - EnableCustomJs bool `koanf:"enableCustomJs"` + BannerCSS string `koanf:"bannerCss"` + BannerMessage string `koanf:"bannerMessage"` + DefaultPopupOnStart string `koanf:"defaultPopupOnStart"` + ServiceHostMode string `koanf:"serviceHostMode"` + DefaultOnClick string `koanf:"defaultOnClick"` AuthJwtCookieName string `koanf:"authJwtCookieName"` AuthJwtHeader string `koanf:"authJwtHeader"` AuthJwtAud string `koanf:"authJwtAud"` - AuthJwtDomain string `koanf:"authJwtDomain"` - AuthJwtCertsURL string `koanf:"authJwtCertsUrl"` - AuthJwtHmacSecret string `koanf:"authJwtHmacSecret"` // mutually exclusive with pub key config fields + ListenAddressWebUI string `koanf:"listenAddressWebUI"` + SectionNavigationStyle string `koanf:"sectionNavigationStyle"` + DefaultIconForDirectories string `koanf:"defaultIconForDirectories"` AuthJwtClaimUsername string `koanf:"authJwtClaimUsername"` AuthJwtClaimUserGroup string `koanf:"authJwtClaimUserGroup"` - AuthJwtPubKeyPath string `koanf:"authJwtPubKeyPath"` // will read pub key from file on disk + AuthJwtPubKeyPath string `koanf:"authJwtPubKeyPath"` AuthHttpHeaderUsername string `koanf:"authHttpHeaderUsername"` AuthHttpHeaderUserGroup string `koanf:"authHttpHeaderUserGroup"` AuthHttpHeaderUserGroupSep string `koanf:"authHttpHeaderUserGroupSep"` - AuthLocalUsers AuthLocalUsersConfig `koanf:"authLocalUsers"` - AuthLoginUrl string `koanf:"authLoginUrl"` - AuthRequireGuestsToLogin bool `koanf:"authRequireGuestsToLogin"` - AuthOAuth2RedirectURL string `koanf:"authOAuth2RedirectUrl"` - AuthOAuth2Providers map[string]*OAuth2Provider `koanf:"authOAuth2Providers"` - DefaultPermissions PermissionsList `koanf:"defaultPermissions"` - DefaultPolicy ConfigurationPolicy `koanf:"defaultPolicy"` - AccessControlLists []*AccessControlList `koanf:"accessControlLists"` WebUIDir string `koanf:"webUIDir"` - CronSupportForSeconds bool `koanf:"cronSupportForSeconds"` - SectionNavigationStyle string `koanf:"sectionNavigationStyle"` - DefaultOnClick string `koanf:"defaultOnClick"` - DefaultPopupOnStart string `koanf:"defaultPopupOnStart"` - InsecureAllowDumpOAuth2UserData bool `koanf:"insecureAllowDumpOAuth2UserData"` - InsecureAllowDumpVars bool `koanf:"insecureAllowDumpVars"` - InsecureAllowDumpServerDiagnostics bool `koanf:"insecureAllowDumpServerDiagnostics"` - InsecureAllowDumpActionMap bool `koanf:"insecureAllowDumpActionMap"` - InsecureAllowDumpJwtClaims bool `koanf:"insecureAllowDumpJwtClaims"` - Prometheus PrometheusConfig `koanf:"prometheus"` + AuthLoginUrl string `koanf:"authLoginUrl"` + AuthJwtDomain string `koanf:"authJwtDomain"` Security SecurityConfig `koanf:"security"` - SaveLogs SaveLogsConfig `koanf:"saveLogs"` - ServiceLogs ServiceLogsConfig `koanf:"serviceLogs"` - DefaultIconForActions string `koanf:"defaultIconForActions"` - DefaultIconForDirectories string `koanf:"defaultIconForDirectories"` - DefaultIconForBack string `koanf:"defaultIconForBack"` - AdditionalNavigationLinks []*NavigationLink `koanf:"additionalNavigationLinks"` - ServiceHostMode string `koanf:"serviceHostMode"` + Actions []*Action `koanf:"actions"` + AccessControlLists []*AccessControlList `koanf:"accessControlLists"` StyleMods []string `koanf:"styleMods"` - BannerMessage string `koanf:"bannerMessage"` - BannerCSS string `koanf:"bannerCss"` - Include string `koanf:"include"` - - sourceFiles []string + AdditionalNavigationLinks []*NavigationLink `koanf:"additionalNavigationLinks"` + Entities []*EntityFile `koanf:"entities"` + Dashboards []*DashboardComponent `koanf:"dashboards"` + sourceFiles []string + AuthLocalUsers AuthLocalUsersConfig `koanf:"authLocalUsers"` + LogHistoryPageSize int64 `koanf:"logHistoryPageSize"` + LogDebugOptions LogDebugOptions `koanf:"logDebugOptions"` + DefaultPermissions PermissionsList `koanf:"defaultPermissions"` + DefaultPolicy ConfigurationPolicy `koanf:"defaultPolicy"` + Prometheus PrometheusConfig `koanf:"prometheus"` + CheckForUpdates bool `koanf:"checkForUpdates"` + InsecureAllowDumpJwtClaims bool `koanf:"insecureAllowDumpJwtClaims"` + InsecureAllowDumpActionMap bool `koanf:"insecureAllowDumpActionMap"` + InsecureAllowDumpServerDiagnostics bool `koanf:"insecureAllowDumpServerDiagnostics"` + InsecureAllowDumpVars bool `koanf:"insecureAllowDumpVars"` + InsecureAllowDumpOAuth2UserData bool `koanf:"insecureAllowDumpOAuth2UserData"` + CronSupportForSeconds bool `koanf:"cronSupportForSeconds"` + AuthRequireGuestsToLogin bool `koanf:"authRequireGuestsToLogin"` + EnableCustomJs bool `koanf:"enableCustomJs"` + ShowNavigateOnStartIcons bool `koanf:"showNavigateOnStartIcons"` + ShowNewVersions bool `koanf:"showNewVersions"` + ShowNavigation bool `koanf:"showNavigation"` + ShowFooter bool `koanf:"showFooter"` + UseSingleHTTPFrontend bool `koanf:"useSingleHTTPFrontend"` + ThemeCacheDisabled bool `koanf:"themeCacheDisabled"` } type AuthLocalUsersConfig struct { - Enabled bool `koanf:"enabled"` Users []*LocalUser `koanf:"users"` + Enabled bool `koanf:"enabled"` } type LocalUser struct { @@ -246,21 +243,21 @@ type LocalUser struct { } type OAuth2Provider struct { - Name string `koanf:"name"` - Title string `koanf:"title"` + AuthUrl string `koanf:"authUrl"` + UserGroupField string `koanf:"userGroupField"` ClientID string `koanf:"clientId"` ClientSecret string `koanf:"clientSecret"` Icon string `koanf:"icon"` - Scopes []string `koanf:"scopes"` - AuthUrl string `koanf:"authUrl"` - TokenUrl string `koanf:"tokenUrl"` - WhoamiUrl string `koanf:"whoamiUrl"` - UsernameField string `koanf:"usernameField"` - UserGroupField string `koanf:"userGroupField"` - InsecureSkipVerify bool `koanf:"insecureSkipVerify"` - CallbackTimeout int `koanf:"callbackTimeout"` - CertBundlePath string `koanf:"certBundlePath"` AddToUsergroup string `koanf:"addToUsergroup"` + Title string `koanf:"title"` + WhoamiUrl string `koanf:"whoamiUrl"` + Name string `koanf:"name"` + UsernameField string `koanf:"usernameField"` + TokenUrl string `koanf:"tokenUrl"` + CertBundlePath string `koanf:"certBundlePath"` + Scopes []string `koanf:"scopes"` + CallbackTimeout int `koanf:"callbackTimeout"` + InsecureSkipVerify bool `koanf:"insecureSkipVerify"` } type NavigationLink struct { diff --git a/service/internal/executor/arguments_test.go b/service/internal/executor/arguments_test.go index 23ae6f5..f4c7882 100644 --- a/service/internal/executor/arguments_test.go +++ b/service/internal/executor/arguments_test.go @@ -981,12 +981,12 @@ func TestTypecheckActionArgumentHtmlWithoutName(t *testing.T) { func TestParseCommandForReplacements(t *testing.T) { tests := []struct { + values map[string]string name string shellCommand string - values map[string]string expectedOutput string - expectError bool errorContains string + expectError bool }{ { name: "Simple replacement", @@ -1052,10 +1052,10 @@ func TestParseCommandForReplacements(t *testing.T) { func TestArgumentChoicesValidation(t *testing.T) { tests := []struct { - name string req *ExecutionRequest - expectError bool + name string description string + expectError bool }{ { name: "Valid choice", diff --git a/service/internal/executor/executor.go b/service/internal/executor/executor.go index 97febe7..6b640cb 100644 --- a/service/internal/executor/executor.go +++ b/service/internal/executor/executor.go @@ -40,53 +40,46 @@ func isValidTrackingID(id string) bool { } type ActionBinding struct { - ID string Action *config.Action Entity *entities.Entity - ConfigOrder int + ID string OnDashboards []DashboardNavigationTarget + ConfigOrder int } // Executor represents a helper class for executing commands. It's main method // is ExecRequest type Executor struct { logs map[string]*InternalLogEntry - logsTrackingIdsByDate []string LogsByBindingId map[string][]*InternalLogEntry - - logmutex sync.RWMutex - MapActionBindings map[string]*ActionBinding + Cfg *config.Config + logsTrackingIdsByDate []string + listeners []listener + chainOfCommand []executorStepFunc + groupQueue []*queuedExecution + logmutex sync.RWMutex MapActionBindingsLock sync.RWMutex - - Cfg *config.Config - - listeners []listener - listenersMu sync.RWMutex - - chainOfCommand []executorStepFunc - - groupQueue []*queuedExecution - groupQueueMu sync.Mutex + listenersMu sync.RWMutex + groupQueueMu sync.Mutex } // ExecutionRequest is a request to execute an action. It's passed to an // Executor. They're created from the api. type ExecutionRequest struct { - Binding *ActionBinding - Arguments map[string]string - TrackingID string - Tags []string - Cfg *config.Config - AuthenticatedUser *authpublic.AuthenticatedUser - TriggerDepth int - Justification string - + Arguments map[string]string + Binding *ActionBinding + Cfg *config.Config + AuthenticatedUser *authpublic.AuthenticatedUser + executor *Executor logEntry *InternalLogEntry finalParsedCommand string + TrackingID string + Justification string + Tags []string execArgs []string + TriggerDepth int useDirectExec bool - executor *Executor skipRequestRegistration bool } @@ -104,12 +97,12 @@ func (req *ExecutionRequest) mutateLogEntry(mutator func(*InternalLogEntry)) { // LogEntrySnapshot is a copy of selected log entry fields for race-safe reads. type LogEntrySnapshot struct { + Output string + ExitCode int32 Queued bool Blocked bool ExecutionStarted bool ExecutionFinished bool - ExitCode int32 - Output string } // SnapshotLog returns a copy of selected log entry fields under read lock. @@ -136,34 +129,28 @@ func (e *Executor) SnapshotLog(trackingID string) (LogEntrySnapshot, bool) { // state of execution (even if the command is not executed). It's designed to be // easily serializable. type InternalLogEntry struct { - Binding *ActionBinding DatetimeStarted time.Time DatetimeFinished time.Time - Output string - TimedOut bool - Blocked bool - Queued bool - QueuedForGroup string - ExitCode int32 - Tags []string - ExecutionStarted bool - ExecutionFinished bool - ExecutionTrackingID string + Binding *ActionBinding Process *os.Process + Arguments map[string]string + ExecutionTrackingID string + Justification string + QueuedForGroup string + ActionIcon string + ActionTitle string + ActionConfigTitle string + Output string Username string - Index int64 EntityPrefix string - ActionConfigTitle string // This is the title of the action as defined in the config, not the final parsed title. - - /* - The following 3 properties are obviously on Action normally, but it's useful - that logs are lightweight (so we don't need to have an action associated to - logs, etc. Therefore, we duplicate those values here. - */ - ActionTitle string - ActionIcon string - Justification string - Arguments map[string]string + Tags []string + Index int64 + ExitCode int32 + Blocked bool + ExecutionFinished bool + ExecutionStarted bool + Queued bool + TimedOut bool } // .Binding can be nil, so we need to handle that. @@ -1098,8 +1085,8 @@ func appendErrorToStderr(req *ExecutionRequest, err error) { type OutputStreamer struct { Req *ExecutionRequest - mu sync.Mutex output bytes.Buffer + mu sync.Mutex } func (ost *OutputStreamer) Write(o []byte) (n int, err error) { diff --git a/service/internal/filehelper/file_change_notify.go b/service/internal/filehelper/file_change_notify.go index 5173903..c7a0d4c 100644 --- a/service/internal/filehelper/file_change_notify.go +++ b/service/internal/filehelper/file_change_notify.go @@ -39,12 +39,12 @@ type WatchMeta struct { } type watchContext struct { - filename string - filedir string callback func(filename string) - interestedEvent fsnotify.Op event *fsnotify.Event meta WatchMeta + filename string + filedir string + interestedEvent fsnotify.Op } func WatchDirectoryCreate(fullpath string, callback func(filename string), meta WatchMeta) { diff --git a/service/internal/installationinfo/runtimeinfo.go b/service/internal/installationinfo/runtimeinfo.go index ea8f74e..b87cfde 100644 --- a/service/internal/installationinfo/runtimeinfo.go +++ b/service/internal/installationinfo/runtimeinfo.go @@ -16,7 +16,6 @@ type RuntimeInfo struct { OS string OSReleasePrettyName string Arch string - InContainer bool LastBrowserUserAgent string User string Uid string @@ -25,6 +24,7 @@ type RuntimeInfo struct { AvailableVersion string WebuiDirectory string ThemesDirectory string + InContainer bool } var Runtime = &RuntimeInfo{ diff --git a/service/internal/installationinfo/server_diagnostics.go b/service/internal/installationinfo/server_diagnostics.go index e2edccd..cd5bc12 100644 --- a/service/internal/installationinfo/server_diagnostics.go +++ b/service/internal/installationinfo/server_diagnostics.go @@ -13,8 +13,6 @@ var ( ) type serverDiagnosticsConfig struct { - CountOfActions int - CountOfDashboards int LogLevel string ListenAddressSingleHTTPFrontend string ListenAddressWebUI string @@ -23,6 +21,8 @@ type serverDiagnosticsConfig struct { TimeNow string ConfigDirectory string WebuiDirectory string + CountOfActions int + CountOfDashboards int } func configToServerDiagnostics(cfg *config.Config) *serverDiagnosticsConfig { diff --git a/service/internal/logfilter/record.go b/service/internal/logfilter/record.go index 6315a7b..648d301 100644 --- a/service/internal/logfilter/record.go +++ b/service/internal/logfilter/record.go @@ -5,12 +5,12 @@ type Record struct { Status string Action string User string + Output string Tags []string + ExitCode int32 Blocked bool TimedOut bool Running bool - ExitCode int32 - Output string } // StatusLabel matches the status text shown in the web UI. diff --git a/service/internal/updatecheck/updateCheck.go b/service/internal/updatecheck/updateCheck.go index 5c35f17..54a697f 100644 --- a/service/internal/updatecheck/updateCheck.go +++ b/service/internal/updatecheck/updateCheck.go @@ -13,9 +13,9 @@ import ( ) type versionMapType struct { - ApiVersion int - Latest string History map[string]string + Latest string + ApiVersion int } // StartUpdateChecker will start a job that runs periodically, checking diff --git a/service/scripts/find-flakey-tests-inf/main.go b/service/scripts/find-flakey-tests-inf/main.go index 173a9f3..34af824 100644 --- a/service/scripts/find-flakey-tests-inf/main.go +++ b/service/scripts/find-flakey-tests-inf/main.go @@ -42,20 +42,20 @@ type runSummary struct { } type jsonlRecord struct { - Run int `json:"run"` Timestamp string `json:"timestamp"` + FailureDetails []testFailure `json:"failureDetails"` + Run int `json:"run"` ExitCode int `json:"exitCode"` DurationMs int64 `json:"durationMs"` Passes int `json:"passes"` Failures int `json:"failures"` Skipped int `json:"skipped"` - FailureDetails []testFailure `json:"failureDetails"` } type testRunState struct { - summary runSummary - failures []testFailure failureOutput map[string]*strings.Builder + failures []testFailure + summary runSummary } func initLog() {