Skip to content

Commit d484497

Browse files
authored
Merge pull request #1640 from kaleido-io/monitoring-routes
Rename metrics to monitoring to support additional routes
2 parents 96eff81 + 2aa4db2 commit d484497

File tree

10 files changed

+168
-61
lines changed

10 files changed

+168
-61
lines changed

Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ ARG UI_TAG
7575
ARG UI_RELEASE
7676
RUN apk add --update --no-cache \
7777
sqlite=3.44.2-r0 \
78-
postgresql16-client=16.6-r0 \
78+
postgresql16-client=16.8-r0 \
7979
curl=8.12.1-r0 \
8080
jq=1.7.1-r0
8181
WORKDIR /firefly

doc-site/docs/reference/config.md

Lines changed: 44 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -407,30 +407,70 @@ title: Configuration Reference
407407

408408
## metrics
409409

410+
|Key|Description|Type|Default Value|
411+
|---|-----------|----|-------------|
412+
|address|Deprecated - use monitoring.address instead|`int`|`127.0.0.1`
413+
|enabled|Deprecated - use monitoring.enabled instead|`boolean`|`true`
414+
|path|Deprecated - use monitoring.metricsPath instead|`string`|`/metrics`
415+
|port|Deprecated - use monitoring.port instead|`int`|`6000`
416+
|publicURL|Deprecated - use monitoring.publicURL instead|URL `string`|`<nil>`
417+
|readTimeout|Deprecated - use monitoring.readTimeout instead|[`time.Duration`](https://pkg.go.dev/time#Duration)|`15s`
418+
|shutdownTimeout|The maximum amount of time to wait for any open HTTP requests to finish before shutting down the HTTP server|[`time.Duration`](https://pkg.go.dev/time#Duration)|`10s`
419+
|writeTimeout|Deprecated - use monitoring.writeTimeout instead|[`time.Duration`](https://pkg.go.dev/time#Duration)|`15s`
420+
421+
## metrics.auth
422+
423+
|Key|Description|Type|Default Value|
424+
|---|-----------|----|-------------|
425+
|type|The auth plugin to use for server side authentication of requests|`string`|`<nil>`
426+
427+
## metrics.auth.basic
428+
429+
|Key|Description|Type|Default Value|
430+
|---|-----------|----|-------------|
431+
|passwordfile|The path to a .htpasswd file to use for authenticating requests. Passwords should be hashed with bcrypt.|`string`|`<nil>`
432+
433+
## metrics.tls
434+
435+
|Key|Description|Type|Default Value|
436+
|---|-----------|----|-------------|
437+
|ca|The TLS certificate authority in PEM format (this option is ignored if caFile is also set)|`string`|`<nil>`
438+
|caFile|The path to the CA file for TLS on this API|`string`|`<nil>`
439+
|cert|The TLS certificate in PEM format (this option is ignored if certFile is also set)|`string`|`<nil>`
440+
|certFile|The path to the certificate file for TLS on this API|`string`|`<nil>`
441+
|clientAuth|Enables or disables client auth for TLS on this API|`string`|`<nil>`
442+
|enabled|Enables or disables TLS on this API|`boolean`|`false`
443+
|insecureSkipHostVerify|When to true in unit test development environments to disable TLS verification. Use with extreme caution|`boolean`|`<nil>`
444+
|key|The TLS certificate key in PEM format (this option is ignored if keyFile is also set)|`string`|`<nil>`
445+
|keyFile|The path to the private key file for TLS on this API|`string`|`<nil>`
446+
|requiredDNAttributes|A set of required subject DN attributes. Each entry is a regular expression, and the subject certificate must have a matching attribute of the specified type (CN, C, O, OU, ST, L, STREET, POSTALCODE, SERIALNUMBER are valid attributes)|`map[string]string`|`<nil>`
447+
448+
## monitoring
449+
410450
|Key|Description|Type|Default Value|
411451
|---|-----------|----|-------------|
412452
|address|The IP address on which the metrics HTTP API should listen|`int`|`127.0.0.1`
413453
|enabled|Enables the metrics API|`boolean`|`true`
414-
|path|The path from which to serve the Prometheus metrics|`string`|`/metrics`
454+
|metricsPath|The path from which to serve the Prometheus metrics|`string`|`/metrics`
415455
|port|The port on which the metrics HTTP API should listen|`int`|`6000`
416456
|publicURL|The fully qualified public URL for the metrics API. This is used for building URLs in HTTP responses and in OpenAPI Spec generation|URL `string`|`<nil>`
417457
|readTimeout|The maximum time to wait when reading from an HTTP connection|[`time.Duration`](https://pkg.go.dev/time#Duration)|`15s`
418458
|shutdownTimeout|The maximum amount of time to wait for any open HTTP requests to finish before shutting down the HTTP server|[`time.Duration`](https://pkg.go.dev/time#Duration)|`10s`
419459
|writeTimeout|The maximum time to wait when writing to an HTTP connection|[`time.Duration`](https://pkg.go.dev/time#Duration)|`15s`
420460

421-
## metrics.auth
461+
## monitoring.auth
422462

423463
|Key|Description|Type|Default Value|
424464
|---|-----------|----|-------------|
425465
|type|The auth plugin to use for server side authentication of requests|`string`|`<nil>`
426466

427-
## metrics.auth.basic
467+
## monitoring.auth.basic
428468

429469
|Key|Description|Type|Default Value|
430470
|---|-----------|----|-------------|
431471
|passwordfile|The path to a .htpasswd file to use for authenticating requests. Passwords should be hashed with bcrypt.|`string`|`<nil>`
432472

433-
## metrics.tls
473+
## monitoring.tls
434474

435475
|Key|Description|Type|Default Value|
436476
|---|-----------|----|-------------|

internal/apiserver/metrics_server.go

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright © 2022 Kaleido, Inc.
1+
// Copyright © 2025 Kaleido, Inc.
22
//
33
// SPDX-License-Identifier: Apache-2.0
44
//
@@ -21,11 +21,17 @@ import (
2121
)
2222

2323
const (
24-
MetricsEnabled = "enabled"
25-
MetricsPath = "path"
24+
Enabled = "enabled"
25+
DeprecatedMetricsPath = "path"
26+
MetricsPath = "metricsPath"
2627
)
2728

28-
func initMetricsConfig(config config.Section) {
29-
config.AddKnownKey(MetricsEnabled, true)
29+
func initDeprecatedMetricsConfig(config config.Section) {
30+
config.AddKnownKey(Enabled, true)
31+
config.AddKnownKey(DeprecatedMetricsPath, "/metrics")
32+
}
33+
34+
func initMonitoringConfig(config config.Section) {
35+
config.AddKnownKey(Enabled, true)
3036
config.AddKnownKey(MetricsPath, "/metrics")
3137
}

internal/apiserver/server.go

Lines changed: 48 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright © 2024 Kaleido, Inc.
1+
// Copyright © 2025 Kaleido, Inc.
22
//
33
// SPDX-License-Identifier: Apache-2.0
44
//
@@ -42,10 +42,11 @@ import (
4242
)
4343

4444
var (
45-
spiConfig = config.RootSection("spi")
46-
apiConfig = config.RootSection("http")
47-
metricsConfig = config.RootSection("metrics")
48-
corsConfig = config.RootSection("cors")
45+
spiConfig = config.RootSection("spi")
46+
apiConfig = config.RootSection("http")
47+
deprecatedMetricsConfig = config.RootSection("metrics")
48+
monitoringConfig = config.RootSection("monitoring")
49+
corsConfig = config.RootSection("cors")
4950
)
5051

5152
// Server is the external interface for the API Server
@@ -55,31 +56,35 @@ type Server interface {
5556

5657
type apiServer struct {
5758
// Defaults set with config
58-
apiTimeout time.Duration
59-
apiMaxTimeout time.Duration
60-
metricsEnabled bool
61-
ffiSwaggerGen FFISwaggerGen
62-
apiPublicURL string
63-
dynamicPublicURLHeader string
64-
defaultNamespace string
59+
apiTimeout time.Duration
60+
apiMaxTimeout time.Duration
61+
deprecatedMetricsEnabled bool
62+
monitoringEnabled bool
63+
ffiSwaggerGen FFISwaggerGen
64+
apiPublicURL string
65+
dynamicPublicURLHeader string
66+
defaultNamespace string
6567
}
6668

6769
func InitConfig() {
6870
httpserver.InitHTTPConfig(apiConfig, 5000)
6971
httpserver.InitHTTPConfig(spiConfig, 5001)
70-
httpserver.InitHTTPConfig(metricsConfig, 6000)
72+
httpserver.InitHTTPConfig(deprecatedMetricsConfig, 6000)
73+
httpserver.InitHTTPConfig(monitoringConfig, 6000)
7174
httpserver.InitCORSConfig(corsConfig)
72-
initMetricsConfig(metricsConfig)
75+
initDeprecatedMetricsConfig(deprecatedMetricsConfig)
76+
initMonitoringConfig(monitoringConfig)
7377
}
7478

7579
func NewAPIServer() Server {
7680
as := &apiServer{
77-
apiTimeout: config.GetDuration(coreconfig.APIRequestTimeout),
78-
apiMaxTimeout: config.GetDuration(coreconfig.APIRequestMaxTimeout),
79-
dynamicPublicURLHeader: config.GetString(coreconfig.APIDynamicPublicURLHeader),
80-
defaultNamespace: config.GetString(coreconfig.NamespacesDefault),
81-
metricsEnabled: config.GetBool(coreconfig.MetricsEnabled),
82-
ffiSwaggerGen: &ffiSwaggerGen{},
81+
apiTimeout: config.GetDuration(coreconfig.APIRequestTimeout),
82+
apiMaxTimeout: config.GetDuration(coreconfig.APIRequestMaxTimeout),
83+
dynamicPublicURLHeader: config.GetString(coreconfig.APIDynamicPublicURLHeader),
84+
defaultNamespace: config.GetString(coreconfig.NamespacesDefault),
85+
deprecatedMetricsEnabled: config.GetBool(coreconfig.DeprecatedMetricsEnabled),
86+
monitoringEnabled: config.GetBool(coreconfig.MonitoringEnabled),
87+
ffiSwaggerGen: &ffiSwaggerGen{},
8388
}
8489
as.apiPublicURL = as.getPublicURL(apiConfig, "")
8590
return as
@@ -110,15 +115,21 @@ func (as *apiServer) Serve(ctx context.Context, mgr namespace.Manager) (err erro
110115
} else if config.GetBool(coreconfig.LegacyAdminEnabled) {
111116
log.L(ctx).Warnf("Your config includes an 'admin' section, which should be renamed to 'spi' - SPI server will not be enabled until this is corrected")
112117
}
118+
serverName := "metrics"
119+
mConfig := deprecatedMetricsConfig
120+
if as.monitoringEnabled {
121+
serverName = "monitoring"
122+
mConfig = monitoringConfig
123+
}
113124

114-
if as.metricsEnabled {
115-
metricsHTTPServer, err := httpserver.NewHTTPServer(ctx, "metrics", as.createMetricsMuxRouter(), metricsErrChan, metricsConfig, corsConfig, &httpserver.ServerOptions{
125+
if as.deprecatedMetricsEnabled || as.monitoringEnabled {
126+
monitoringServer, err := httpserver.NewHTTPServer(ctx, serverName, as.createMonitoringMuxRouter(), metricsErrChan, mConfig, corsConfig, &httpserver.ServerOptions{
116127
MaximumRequestTimeout: as.apiMaxTimeout,
117128
})
118129
if err != nil {
119130
return err
120131
}
121-
go metricsHTTPServer.ServeHTTP(ctx)
132+
go monitoringServer.ServeHTTP(ctx)
122133
}
123134

124135
return as.waitForServerStop(httpErrChan, spiErrChan, metricsErrChan)
@@ -345,7 +356,7 @@ func (as *apiServer) createMuxRouter(ctx context.Context, mgr namespace.Manager)
345356
r := mux.NewRouter()
346357
hf := as.handlerFactory()
347358

348-
if as.metricsEnabled {
359+
if as.deprecatedMetricsEnabled || as.monitoringEnabled {
349360
r.Use(metrics.GetRestServerInstrumentation().Middleware)
350361
}
351362

@@ -433,7 +444,7 @@ func (as *apiServer) spiWSHandler(mgr namespace.Manager) http.HandlerFunc {
433444

434445
func (as *apiServer) createAdminMuxRouter(mgr namespace.Manager) *mux.Router {
435446
r := mux.NewRouter()
436-
if as.metricsEnabled {
447+
if as.deprecatedMetricsEnabled || as.monitoringEnabled {
437448
r.Use(metrics.GetAdminServerInstrumentation().Middleware)
438449
}
439450
hf := as.handlerFactory()
@@ -468,12 +479,20 @@ func (as *apiServer) createAdminMuxRouter(mgr namespace.Manager) *mux.Router {
468479
return r
469480
}
470481

471-
func (as *apiServer) createMetricsMuxRouter() *mux.Router {
482+
func (as *apiServer) createMonitoringMuxRouter() *mux.Router {
472483
r := mux.NewRouter()
473-
474-
r.Path(config.GetString(coreconfig.MetricsPath)).Handler(promhttp.InstrumentMetricHandler(metrics.Registry(),
484+
metricsPath := config.GetString(coreconfig.DeprecatedMetricsPath)
485+
if as.monitoringEnabled {
486+
metricsPath = config.GetString(coreconfig.MonitoringMetricsPath)
487+
}
488+
r.Path(metricsPath).Handler(promhttp.InstrumentMetricHandler(metrics.Registry(),
475489
promhttp.HandlerFor(metrics.Registry(), promhttp.HandlerOpts{})))
476-
490+
hf := ffapi.HandlerFactory{}
491+
r.HandleFunc("/livez", hf.APIWrapper(func(res http.ResponseWriter, req *http.Request) (status int, err error) {
492+
// a simple liveness check
493+
return http.StatusOK, nil
494+
}))
495+
r.NotFoundHandler = hf.APIWrapper(as.notFoundHandler)
477496
return r
478497
}
479498

internal/apiserver/server_test.go

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright © 2021 Kaleido, Inc.
1+
// Copyright © 2025 Kaleido, Inc.
22
//
33
// SPDX-License-Identifier: Apache-2.0
44
//
@@ -86,7 +86,7 @@ func TestStartStopServer(t *testing.T) {
8686
InitConfig()
8787
apiConfig.Set(httpserver.HTTPConfPort, 0)
8888
spiConfig.Set(httpserver.HTTPConfPort, 0)
89-
metricsConfig.Set(httpserver.HTTPConfPort, 0)
89+
monitoringConfig.Set(httpserver.HTTPConfPort, 0)
9090
config.Set(coreconfig.UIPath, "test")
9191
config.Set(coreconfig.SPIEnabled, true)
9292
ctx, cancel := context.WithCancel(context.Background())
@@ -105,7 +105,7 @@ func TestStartLegacyAdminConfig(t *testing.T) {
105105
InitConfig()
106106
apiConfig.Set(httpserver.HTTPConfPort, 0)
107107
spiConfig.Set(httpserver.HTTPConfPort, 0)
108-
metricsConfig.Set(httpserver.HTTPConfPort, 0)
108+
monitoringConfig.Set(httpserver.HTTPConfPort, 0)
109109
config.Set(coreconfig.UIPath, "test")
110110
config.Set(coreconfig.LegacyAdminEnabled, true)
111111
ctx, cancel := context.WithCancel(context.Background())
@@ -170,8 +170,8 @@ func TestStartMetricsFail(t *testing.T) {
170170
coreconfig.Reset()
171171
metrics.Clear()
172172
InitConfig()
173-
metricsConfig.Set(httpserver.HTTPConfAddress, "...://")
174-
config.Set(coreconfig.MetricsEnabled, true)
173+
monitoringConfig.Set(httpserver.HTTPConfAddress, "...://")
174+
config.Set(coreconfig.MonitoringEnabled, true)
175175
ctx, cancel := context.WithCancel(context.Background())
176176
cancel() // server will immediately shut down
177177
as := NewAPIServer()
@@ -575,3 +575,17 @@ func TestContractAPIDefaultNS(t *testing.T) {
575575
assert.NoError(t, err)
576576
assert.Equal(t, 200, res.StatusCode())
577577
}
578+
579+
func TestMonitoringServerRoutes(t *testing.T) {
580+
_, _, as := newTestServer()
581+
s := httptest.NewServer(as.createMonitoringMuxRouter())
582+
defer s.Close()
583+
584+
res, err := http.Get(fmt.Sprintf("http://%s/livez", s.Listener.Addr()))
585+
assert.NoError(t, err)
586+
assert.Equal(t, 200, res.StatusCode)
587+
588+
res, err = http.Get(fmt.Sprintf("http://%s/metrics", s.Listener.Addr()))
589+
assert.NoError(t, err)
590+
assert.Equal(t, 200, res.StatusCode)
591+
}

internal/coreconfig/coreconfig.go

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright © 2024 Kaleido, Inc.
1+
// Copyright © 2025 Kaleido, Inc.
22
//
33
// SPDX-License-Identifier: Apache-2.0
44
//
@@ -287,10 +287,14 @@ var (
287287
MessageWriterBatchTimeout = ffc("message.writer.batchTimeout")
288288
// MessageWriterBatchMaxInserts
289289
MessageWriterBatchMaxInserts = ffc("message.writer.batchMaxInserts")
290-
// MetricsEnabled determines whether metrics will be instrumented and if the metrics server will be enabled or not
291-
MetricsEnabled = ffc("metrics.enabled")
290+
// MetricsEnabled - deprecated, use monitoring.enabled
291+
DeprecatedMetricsEnabled = ffc("metrics.enabled")
292+
// MetricsPath - deprecated, use monitoring.metricsPath
293+
DeprecatedMetricsPath = ffc("metrics.path")
294+
// Monitoring determines whether monitoring routes will be enabled, which contains metrics instruments
295+
MonitoringEnabled = ffc("monitoring.enabled")
292296
// MetricsPath determines what path to serve the Prometheus metrics from
293-
MetricsPath = ffc("metrics.path")
297+
MonitoringMetricsPath = ffc("monitoring.metricsPath")
294298
// NamespacesDefault is the default namespace - must be in the predefines list
295299
NamespacesDefault = ffc("namespaces.default")
296300
// NamespacesPredefined is a list of namespaces to ensure exists, without requiring a broadcast from the network

internal/coremsgs/en_config_descriptions.go

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright © 2024 Kaleido, Inc.
1+
// Copyright © 2025 Kaleido, Inc.
22
//
33
// SPDX-License-Identifier: Apache-2.0
44
//
@@ -320,13 +320,21 @@ var (
320320
ConfigTransactionWriterBatchTimeout = ffc("config.transaction.writer.batchTimeout", "How long to wait for more transactions to arrive before flushing the batch", i18n.TimeDurationType)
321321
ConfigTransactionWriterCount = ffc("config.transaction.writer.count", "The number of message writer workers", i18n.IntType)
322322

323-
ConfigMetricsAddress = ffc("config.metrics.address", "The IP address on which the metrics HTTP API should listen", i18n.IntType)
324-
ConfigMetricsEnabled = ffc("config.metrics.enabled", "Enables the metrics API", i18n.BooleanType)
325-
ConfigMetricsPath = ffc("config.metrics.path", "The path from which to serve the Prometheus metrics", i18n.StringType)
326-
ConfigMetricsPort = ffc("config.metrics.port", "The port on which the metrics HTTP API should listen", i18n.IntType)
327-
ConfigMetricsPublicURL = ffc("config.metrics.publicURL", "The fully qualified public URL for the metrics API. This is used for building URLs in HTTP responses and in OpenAPI Spec generation", urlStringType)
328-
ConfigMetricsReadTimeout = ffc("config.metrics.readTimeout", "The maximum time to wait when reading from an HTTP connection", i18n.TimeDurationType)
329-
ConfigMetricsWriteTimeout = ffc("config.metrics.writeTimeout", "The maximum time to wait when writing to an HTTP connection", i18n.TimeDurationType)
323+
DeprecatedConfigMetricsAddress = ffc("config.metrics.address", "Deprecated - use monitoring.address instead", i18n.IntType)
324+
DeprecatedConfigMetricsEnabled = ffc("config.metrics.enabled", "Deprecated - use monitoring.enabled instead", i18n.BooleanType)
325+
DeprecatedConfigMetricsPath = ffc("config.metrics.path", "Deprecated - use monitoring.metricsPath instead", i18n.StringType)
326+
DeprecatedConfigMetricsPort = ffc("config.metrics.port", "Deprecated - use monitoring.port instead", i18n.IntType)
327+
DeprecatedConfigMetricsPublicURL = ffc("config.metrics.publicURL", "Deprecated - use monitoring.publicURL instead", urlStringType)
328+
DeprecatedConfigMetricsReadTimeout = ffc("config.metrics.readTimeout", "Deprecated - use monitoring.readTimeout instead", i18n.TimeDurationType)
329+
DeprecatedConfigMetricsWriteTimeout = ffc("config.metrics.writeTimeout", "Deprecated - use monitoring.writeTimeout instead", i18n.TimeDurationType)
330+
331+
ConfigMetricsAddress = ffc("config.monitoring.address", "The IP address on which the metrics HTTP API should listen", i18n.IntType)
332+
ConfigMetricsEnabled = ffc("config.monitoring.enabled", "Enables the metrics API", i18n.BooleanType)
333+
ConfigMetricsPath = ffc("config.monitoring.metricsPath", "The path from which to serve the Prometheus metrics", i18n.StringType)
334+
ConfigMetricsPort = ffc("config.monitoring.port", "The port on which the metrics HTTP API should listen", i18n.IntType)
335+
ConfigMetricsPublicURL = ffc("config.monitoring.publicURL", "The fully qualified public URL for the metrics API. This is used for building URLs in HTTP responses and in OpenAPI Spec generation", urlStringType)
336+
ConfigMetricsReadTimeout = ffc("config.monitoring.readTimeout", "The maximum time to wait when reading from an HTTP connection", i18n.TimeDurationType)
337+
ConfigMetricsWriteTimeout = ffc("config.monitoring.writeTimeout", "The maximum time to wait when writing to an HTTP connection", i18n.TimeDurationType)
330338

331339
ConfigNamespacesDefault = ffc("config.namespaces.default", "The default namespace - must be in the predefined list", i18n.StringType)
332340
ConfigNamespacesPredefined = ffc("config.namespaces.predefined", "A list of namespaces to ensure exists, without requiring a broadcast from the network", "List "+i18n.StringType)

internal/events/webhooks/webhooks_test.go

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -481,7 +481,23 @@ func TestRequestWithBodyReplyEndToEndWithTLS(t *testing.T) {
481481
}()
482482

483483
server.Handler = r
484-
go server.ListenAndServeTLS(publicKeyFile.Name(), privateKeyFile.Name())
484+
go func() {
485+
if err := server.ListenAndServeTLS(publicKeyFile.Name(), privateKeyFile.Name()); err != nil && err != http.ErrServerClosed {
486+
log.Fatalf("ListenAndServeTLS(): %v", err)
487+
}
488+
}()
489+
490+
// Wait for the server to be ready
491+
for {
492+
conn, err := tls.Dial("tcp", server.Addr, &tls.Config{
493+
InsecureSkipVerify: true,
494+
})
495+
if err == nil {
496+
conn.Close()
497+
break
498+
}
499+
time.Sleep(10 * time.Millisecond)
500+
}
485501

486502
// Build a TLS config for the client and set on the subscription object
487503
cert, err := tls.LoadX509KeyPair(publicKeyFile.Name(), privateKeyFile.Name())

0 commit comments

Comments
 (0)