Skip to content

Commit f3e3821

Browse files
committed
merge from develop/6
2 parents dac4795 + d48061a commit f3e3821

115 files changed

Lines changed: 1557 additions & 2527 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.appveyor.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ branches:
77
- /release\/[0-9.]+/
88

99
image:
10-
- Visual Studio 2015
10+
- Visual Studio 2022
1111
# - Visual Studio 2017
1212

1313
cache:
@@ -16,7 +16,7 @@ cache:
1616
environment:
1717
GOPATH: c:\gopath
1818
GOROOT: c:\Program Files\Go
19-
GOVERSION: 1.21.11
19+
GOVERSION: 1.26.0
2020
GO111MODULE: 'on'
2121
GOPROXY: 'https://proxy.golang.org'
2222

@@ -28,7 +28,7 @@ environment:
2828

2929
install:
3030
- rmdir c:\go /s /q
31-
- appveyor DownloadFile https://storage.googleapis.com/golang/go%GOVERSION%.windows-%GOARCH%.msi
31+
- appveyor DownloadFile https://go.dev/dl/go%GOVERSION%.windows-%GOARCH%.msi
3232
- msiexec /i go%GOVERSION%.windows-%GOARCH%.msi /q
3333
- echo %PATH%
3434
- echo %GOPATH%

.circleci/config.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ orbs:
66
parameters:
77
go_version:
88
type: string
9-
default: "1.21.11"
9+
default: "1.26.0"
1010

1111
sensu_go_build_env: &sensu_go_build_env
1212
docker:

.github/workflows/static-check.yml

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,17 +12,19 @@ jobs:
1212
staticcheck:
1313
name: staticcheck (project)
1414
runs-on: ubuntu-latest
15+
env:
16+
GO_VERSION: 1.26.0
1517
steps:
16-
- uses: actions/checkout@v3
18+
- uses: actions/checkout@v4
1719
with:
1820
fetch-depth: 1
19-
- uses: WillAbides/setup-go-faster@v1.14.0
21+
- uses: actions/setup-go@v5
2022
with:
21-
go-version: "1.21.x"
23+
go-version: "1.26.x"
2224
- uses: dominikh/staticcheck-action@v1.3.1
2325
with:
2426
version: "2025.1.1"
2527
install-go: false
26-
cache-key: "1.21.x"
28+
cache-key: "1.26.x"
2729
env:
28-
GO_VERSION: 1.21.3
30+
GO_VERSION: 1.26.0

CHANGELOG-6.md

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,44 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/).
66
and this project adheres to [Semantic
77
Versioning](http://semver.org/spec/v2.0.0.html).
8+
## [Unreleased]
9+
10+
### Added
11+
- Added backend-side validation for `runtime_assets` during CheckConfig creation and update.
12+
- If any referenced asset does not exist in the namespace, the request will be rejected with a validation error.
13+
- This change improves usability by catching missing or invalid assets early, preventing runtime execution failures and reducing debugging overhead.
14+
- Added `ttl_status` field to `Check` and `CheckConfig` to configure the status (warning or critical) for TTL failure events. This field accepts values 1 (warning) or 2 (critical), with critical (2) as the default to maintain backward compatibility.
15+
- Added asset lifecycle management with `last-accessed` timestamp tracking to enable intelligent cleanup of unused assets based on usage patterns.
16+
- Added plugin-based hook system for agent extensions with `BeforeAgentRun` and `AfterAgentRun` lifecycle hooks, supporting priority-based execution ordering and configurable error handling.
17+
- Added `assets-cleanup-interval` and `assets-cleanup-max-age` configuration options for controlling periodic asset cleanup behavior.
18+
- Added `FindUnusedAssets()` and `DeleteAsset()` functions for programmatic asset cleanup operations.
19+
- Added `GetAPIRouter()` and `GetDB()` methods to agent for plugin access to HTTP router and database connection.
20+
- Added `continue_on_error` field to `core/v2.Pipeline`.
21+
This flag controls whether pipeline execution should continue when an error occurs in a handler, filter, mutator, or during asset resolution.
22+
Defaults to `false`, preserving existing behavior.
23+
- Added `ttl_status` field to `Check` and `CheckConfig` to configure the status (warning or critical) for TTL failure events. This field accepts values 1 (warning) or 2 (critical), with warning (1) as the default to maintain backward compatibility.
24+
- Added Prometheus metrics collection for HTTP API requests, including request count (`sensu_go_http_requests_total`), request duration (`sensu_go_http_request_duration_seconds`), and client error count (`sensu_go_http_client_errors_total`) for better observability of backend API performance.
25+
26+
### Changed
27+
- Improved asset error handling with new `AgentAssetError` interface providing structured error reporting with exit codes and detailed messages.
28+
- Enhanced asset manager architecture by restructuring initialization to support pre-initialized database connections and better enterprise integration.
29+
- Improved command execution timeout error handling for more reliable test execution.
30+
31+
### Fixed
32+
- Fixed asset retrieval error aggregation to continue processing remaining assets when individual assets fail, using `multierr` for comprehensive error reporting.
33+
- Upgraded Go version to 1.24.3 across all CI/CD pipelines
34+
- Updated CircleCI, AppVeyor, and GitHub Actions to use Go 1.24.x
35+
- Updated minimum Go requirement in README from 1.16 to 1.24
36+
37+
### Fixed
38+
- Resolved Go 1.24 compatibility issues in agent version and event tests
39+
- Fixed non-constant format string error in pipeline filter legacy code
40+
- Updated `GetAssets` method to return an error when one or more required assets are missing.
41+
When `continue_on_error` is `false`, missing assets or other errors will stop the pipeline execution (existing behavior).
42+
When set to `true`, the pipeline will continue executing subsequent workflows until one succeeds.
43+
- Modified handler execution behavior to return a non-zero status code on failure.
44+
Previously, handler errors were only logged without affecting pipeline.
45+
846
## [6.13.1] - 2025-05-28
947

1048
### Fixed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ to get started with one of Sensu's [supported packages](https://docs.sensu.io/se
4545
### Building from source
4646

4747
The various components of Sensu Go can be manually built from this repository.
48-
You will first need [Go >= 1.16](https://golang.org/doc/install#install)
48+
You will first need [Go >= 1.24](https://golang.org/doc/install#install)
4949
installed. Then, you should clone this repository **outside** of the GOPATH
5050
since Sensu Go uses [Go Modules](https://github.com/golang/go/wiki/Modules):
5151
```

agent/agent.go

Lines changed: 116 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,21 +10,23 @@ import (
1010
"encoding/json"
1111
"errors"
1212
"fmt"
13-
"io/ioutil"
1413
"math/rand"
1514
"net/http"
1615
"net/url"
1716
"os"
1817
"os/signal"
18+
"sort"
1919
"strings"
2020
"sync"
2121
"syscall"
2222

2323
time "github.com/echlebek/timeproxy"
2424
"github.com/gogo/protobuf/proto"
2525
"github.com/google/uuid"
26+
"github.com/gorilla/mux"
2627
"github.com/prometheus/client_golang/prometheus"
2728
"github.com/prometheus/client_golang/prometheus/promhttp"
29+
"go.uber.org/multierr"
2830
"golang.org/x/time/rate"
2931

3032
corev2 "github.com/sensu/core/v2"
@@ -38,6 +40,7 @@ import (
3840
"github.com/sensu/sensu-go/util/retry"
3941
utilstrings "github.com/sensu/sensu-go/util/strings"
4042
"github.com/sirupsen/logrus"
43+
bolt "go.etcd.io/bbolt"
4144
)
4245

4346
const (
@@ -134,11 +137,45 @@ func GetDefaultAgentName() string {
134137
return defaultAgentName
135138
}
136139

140+
// Plugin is the hook-based plugin interface.
141+
type Plugin interface {
142+
// Name returns a human-friendly plugin name.
143+
Name() string
144+
145+
// Priority controls ordering; higher runs earlier in BeforeRun and later in AfterRun.
146+
// Return 0 for default priority.
147+
Priority() int
148+
149+
// BeforeAgentRun is executed before Agent does its core work.
150+
// If it returns an error, Agent decides whether to abort or continue.
151+
BeforeAgentRun(ctx context.Context, a *Agent) error
152+
153+
// AfterAgentRun is executed after Agent's core work (even when core returns error).
154+
// It's good for cleanup or metrics; errors should be reported but usually shouldn't abort.
155+
AfterAgentRun(ctx context.Context, a *Agent) error
156+
157+
// IsFatal optional static policy: if true, any error from this plugin is fatal by default
158+
// Implementors can return false (default).
159+
IsFatal() bool
160+
}
161+
162+
// BasePlugin small helper so implementers don't need to provide all methods.
163+
type BasePlugin struct{}
164+
165+
func (BasePlugin) Name() string { return "base" }
166+
func (BasePlugin) Priority() int { return 0 }
167+
func (BasePlugin) BeforeAgentRun(ctx context.Context, a *Agent) error { return nil }
168+
func (BasePlugin) AfterAgentRun(ctx context.Context, a *Agent) error { return nil }
169+
func (BasePlugin) IsFatal() bool { return false }
170+
137171
// An Agent receives and acts on messages from a Sensu Backend.
138172
type Agent struct {
139173
allowList []allowList
140174
api *http.Server
175+
apiRouter *mux.Router
176+
dbConn *bolt.DB
141177
assetGetter asset.Getter
178+
plugins []Plugin
142179
backendSelector BackendSelector
143180
config *Config
144181
connected bool
@@ -207,6 +244,17 @@ func NewAgentContext(ctx context.Context, config *Config) (*Agent, error) {
207244
maxSessionLength: config.MaxSessionLength,
208245
}
209246

247+
// Prepare the HTTP API server
248+
apiServer, apiRouter := newServer(agent)
249+
agent.api = apiServer
250+
agent.apiRouter = apiRouter
251+
// Prepare the BoltDB before Run
252+
db, dbErr := asset.GetBoltDBConnection(agent.config.CacheDir)
253+
if dbErr != nil {
254+
return nil, dbErr
255+
}
256+
agent.dbConn = db
257+
210258
agent.statsdServer = NewStatsdServer(agent)
211259
agent.handler.AddHandler(transport.MessageTypeEntityConfig, agent.handleEntityConfig)
212260

@@ -224,7 +272,7 @@ func NewAgentContext(ctx context.Context, config *Config) (*Agent, error) {
224272
return nil, fmt.Errorf("error creating agent: %s", err)
225273
}
226274

227-
allowList, err := readAllowList(config.AllowList, ioutil.ReadFile)
275+
allowList, err := readAllowList(config.AllowList, os.ReadFile)
228276
if err != nil {
229277
return nil, err
230278
}
@@ -239,6 +287,10 @@ func NewAgentContext(ctx context.Context, config *Config) (*Agent, error) {
239287
return agent, nil
240288
}
241289

290+
func (a *Agent) GetConfig() *Config {
291+
return a.config
292+
}
293+
242294
func (a *Agent) sendMessage(msg *transport.Message) {
243295
logger.WithFields(logrus.Fields{
244296
"type": msg.Type,
@@ -313,6 +365,11 @@ func (a *Agent) buildTransportHeaderMap() http.Header {
313365
return header
314366
}
315367

368+
// RegisterPlugin registers an agent's plugin
369+
func (a *Agent) RegisterPlugin(p Plugin) {
370+
a.plugins = append(a.plugins, p)
371+
}
372+
316373
// Run starts the Agent.
317374
//
318375
// 1. Start the asset manager.
@@ -332,17 +389,37 @@ func (a *Agent) Run(ctx context.Context) error {
332389
}
333390
}()
334391
defer cancel()
392+
393+
if len(a.plugins) > 0 {
394+
// Keep list sorted descending by Priority (higher first)
395+
sort.SliceStable(a.plugins, func(i, j int) bool {
396+
return a.plugins[i].Priority() > a.plugins[j].Priority()
397+
})
398+
}
399+
400+
var beforeErrs error
401+
// Call BeforeAgentRun in order
402+
for _, p := range a.plugins {
403+
if err := p.BeforeAgentRun(ctx, a); err != nil {
404+
bErr := fmt.Errorf("plugin %s BeforeRun: %w", p.Name(), err)
405+
beforeErrs = multierr.Append(beforeErrs, bErr)
406+
if p.IsFatal() {
407+
return beforeErrs
408+
}
409+
}
410+
}
411+
if beforeErrs != nil {
412+
logger.WithError(beforeErrs).Warn("some plugin BeforeAgentRun errors occurred")
413+
}
414+
335415
a.header = a.buildTransportHeaderMap()
336416

337417
// Fail the agent after startup if the id is invalid
338418
logger.Debug("validating agent name")
339419
if err := corev2.ValidateName(a.config.AgentName); err != nil {
340420
return fmt.Errorf("invalid agent name: %v", err)
341421
}
342-
logger.Debug("validating keepalive warning timeout")
343-
if timeout := a.config.KeepaliveWarningTimeout; timeout < 5 {
344-
return fmt.Errorf("bad keepalive timeout: %d (minimum value is 5 seconds)", timeout)
345-
}
422+
346423
logger.Debug("validating keepalive critical timeout")
347424
if timeout := a.config.KeepaliveCriticalTimeout; timeout > 0 && timeout < 5 {
348425
return fmt.Errorf("bad keepalive critical timeout: %d (minimum value is 5 seconds)", timeout)
@@ -387,7 +464,7 @@ func (a *Agent) Run(ctx context.Context) error {
387464
limit = rate.Limit(asset.DefaultAssetsRateLimit)
388465
}
389466
var err error
390-
a.assetGetter, err = assetManager.StartAssetManager(ctx, rate.NewLimiter(limit, a.config.AssetsBurstLimit))
467+
a.assetGetter, err = assetManager.StartAssetManager(ctx, a.dbConn, rate.NewLimiter(limit, a.config.AssetsBurstLimit))
391468
if err != nil {
392469
return err
393470
}
@@ -417,6 +494,20 @@ func (a *Agent) Run(ctx context.Context) error {
417494
// Wait for context to complete
418495
<-ctx.Done()
419496
logger.Info("agent shutting down")
497+
// Call AfterAgentRun in reverse order (best effort)
498+
var afterErrs error
499+
for i := len(a.plugins) - 1; i >= 0; i-- {
500+
if err := a.plugins[i].AfterAgentRun(ctx, a); err != nil {
501+
aErr := fmt.Errorf("plugin %s AfterRun: %w", a.plugins[i].Name(), err)
502+
afterErrs = multierr.Append(afterErrs, aErr)
503+
if a.plugins[i].IsFatal() {
504+
return afterErrs
505+
}
506+
}
507+
}
508+
if afterErrs != nil {
509+
logger.WithError(afterErrs).Warn("some plugin AfterAgentRun errors occurred")
510+
}
420511

421512
// Wait for all goroutines to gracefully shutdown, but not too long
422513
done := make(chan struct{})
@@ -694,9 +785,6 @@ func (a *Agent) Connected() bool {
694785
// StartAPI starts the Agent HTTP API. After attempting to start the API, if the
695786
// HTTP server encounters a fatal error, it will shutdown the rest of the agent.
696787
func (a *Agent) StartAPI(ctx context.Context) {
697-
// Prepare the HTTP API server
698-
a.api = newServer(a)
699-
700788
// Allow Stop() to block until the HTTP server shuts down.
701789
a.wg.Add(2)
702790

@@ -815,6 +903,24 @@ func (a *Agent) connectWithBackoff(ctx context.Context) (transport.Transport, er
815903
return conn, err
816904
}
817905

906+
// GetDB returns the underlying BoltDB connection used by the agent.
907+
//
908+
// The returned pointer must not be closed or replaced by callers.
909+
// It is intended for read and write operations performed by plugins
910+
// or internal agent components.
911+
func (a *Agent) GetDB() *bolt.DB {
912+
return a.dbConn
913+
}
914+
915+
// GetAPIRouter returns the HTTP router used by the agent for registering
916+
// API endpoints.
917+
//
918+
// Plugins or internal agent components may use this router to mount their
919+
// own routes, handlers, or middleware. Callers should not replace the router instance.
920+
func (a *Agent) GetAPIRouter() *mux.Router {
921+
return a.apiRouter
922+
}
923+
818924
// GracefulShutdown listens for the SIGINT & SIGTERM signals and cancel the
819925
// contexts once a signal is received.
820926
func GracefulShutdown(cancel context.CancelFunc) {

agent/api.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ type sensuVersion struct {
3030
}
3131

3232
// newServer returns a new HTTP server
33-
func newServer(a *Agent) *http.Server {
33+
func newServer(a *Agent) (*http.Server, *mux.Router) {
3434
router := mux.NewRouter()
3535
registerRoutes(a, router)
3636

@@ -41,7 +41,7 @@ func newServer(a *Agent) *http.Server {
4141
ReadTimeout: 15 * time.Second,
4242
}
4343

44-
return server
44+
return server, router
4545
}
4646

4747
func registerRoutes(a *Agent, r *mux.Router) {

agent/api_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ func TestHealthz(t *testing.T) {
113113

114114
func TestVersion(t *testing.T) {
115115
var (
116-
versionResponse = `{"version":""}`
116+
versionResponse = `{"version":"(devel)"}`
117117
)
118118

119119
testCases := []struct {

0 commit comments

Comments
 (0)