This commit is contained in:
Ingmar Stein 2026-02-22 14:58:23 -08:00 committed by GitHub
commit cd3685e214
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 83 additions and 39 deletions

View File

@ -445,13 +445,15 @@ func StartServer(
return err return err
} }
metricsListener, err := metrics.CreateMetricsListener(&listeners, c.String("metrics")) metricsListeners, err := metrics.CreateMetricsListener(&listeners, c.StringSlice("metrics"))
if err != nil { if err != nil {
log.Err(err).Msg("Error opening metrics server listener") log.Err(err).Msg("Error opening metrics server listener")
return errors.Wrap(err, "Error opening metrics server listener") return errors.Wrap(err, "Error opening metrics server listener")
} }
defer metricsListener.Close() for _, l := range metricsListeners {
defer l.Close()
}
wg.Add(1) wg.Add(1)
go func() { go func() {
@ -484,7 +486,7 @@ func StartServer(
QuickTunnelHostname: quickTunnelURL, QuickTunnelHostname: quickTunnelURL,
Orchestrator: orchestrator, Orchestrator: orchestrator,
} }
errC <- metrics.ServeMetrics(metricsListener, ctx, metricsConfig, log) errC <- metrics.ServeMetrics(metricsListeners, ctx, metricsConfig, log)
}() }()
reconnectCh := make(chan supervisor.ReconnectSignal, c.Int(cfdflags.HaConnections)) reconnectCh := make(chan supervisor.ReconnectSignal, c.Int(cfdflags.HaConnections))
@ -877,9 +879,9 @@ func configureCloudflaredFlags(shouldHide bool) []cli.Flag {
Value: false, Value: false,
Hidden: shouldHide, Hidden: shouldHide,
}), }),
altsrc.NewStringFlag(&cli.StringFlag{ altsrc.NewStringSliceFlag(&cli.StringSliceFlag{
Name: cfdflags.Metrics, Name: cfdflags.Metrics,
Value: metrics.GetMetricsDefaultAddress(metrics.Runtime), Value: cli.NewStringSlice(metrics.GetMetricsDefaultAddress(metrics.Runtime)),
Usage: fmt.Sprintf( Usage: fmt.Sprintf(
`Listen address for metrics reporting. If no address is passed cloudflared will try to bind to %v. `Listen address for metrics reporting. If no address is passed cloudflared will try to bind to %v.
If all are unavailable, a random port will be used. Note that when running cloudflared from an virtual If all are unavailable, a random port will be used. Note that when running cloudflared from an virtual

View File

@ -478,10 +478,11 @@ func buildReadyCommand() *cli.Command {
} }
func readyCommand(c *cli.Context) error { func readyCommand(c *cli.Context) error {
metricsOpts := c.String(flags.Metrics) metricsAddrs := c.StringSlice(flags.Metrics)
if !c.IsSet(flags.Metrics) { if len(metricsAddrs) == 0 {
return errors.New("--metrics has to be provided") return errors.New("--metrics has to be provided")
} }
metricsOpts := metricsAddrs[0]
requestURL := fmt.Sprintf("http://%s/ready", metricsOpts) requestURL := fmt.Sprintf("http://%s/ready", metricsOpts)
req, err := http.NewRequest(http.MethodGet, requestURL, nil) req, err := http.NewRequest(http.MethodGet, requestURL, nil)

View File

@ -99,44 +99,53 @@ func newMetricsHandler(
return router return router
} }
// CreateMetricsListener will create a new [net.Listener] by using an // CreateMetricsListener will create a new set of [net.Listener] by using an
// known set of ports when the default address is passed with the fallback // known set of ports when the default address is passed with the fallback
// of choosing a random port when none is available. // of choosing a random port when none is available.
// //
// In case the provided address is not the default one then it will be used // In case the provided address is not the default one then it will be used
// as is. // as is.
func CreateMetricsListener(listeners *gracenet.Net, laddr string) (net.Listener, error) { func CreateMetricsListener(listeners *gracenet.Net, laddrs []string) ([]net.Listener, error) {
if laddr == GetMetricsDefaultAddress(Runtime) { // If the user didn't provide any address, use the default one
if len(laddrs) == 1 && laddrs[0] == GetMetricsDefaultAddress(Runtime) {
// On the presence of the default address select // On the presence of the default address select
// a port from the known set of addresses iteratively. // a port from the known set of addresses iteratively.
addresses := GetMetricsKnownAddresses(Runtime) addresses := GetMetricsKnownAddresses(Runtime)
for _, address := range addresses { for _, address := range addresses {
listener, err := listeners.Listen("tcp", address) listener, err := listeners.Listen("tcp", address)
if err == nil { if err == nil {
return listener, nil return []net.Listener{listener}, nil
} }
} }
// When no port is available then bind to a random one // When no port is available then bind to a random one
listener, err := listeners.Listen("tcp", laddr) listener, err := listeners.Listen("tcp", laddrs[0])
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to listen to default metrics address: %w", err) return nil, fmt.Errorf("failed to listen to default metrics address: %w", err)
} }
return listener, nil return []net.Listener{listener}, nil
} }
// Explicitly got a local address then bind to it // Explicitly got local addresses then bind to them
var ls []net.Listener
for _, laddr := range laddrs {
listener, err := listeners.Listen("tcp", laddr) listener, err := listeners.Listen("tcp", laddr)
if err != nil { if err != nil {
// Close already opened listeners
for _, l := range ls {
l.Close()
}
return nil, fmt.Errorf("failed to bind to address (%s): %w", laddr, err) return nil, fmt.Errorf("failed to bind to address (%s): %w", laddr, err)
} }
ls = append(ls, listener)
}
return listener, nil return ls, nil
} }
func ServeMetrics( func ServeMetrics(
l net.Listener, listeners []net.Listener,
ctx context.Context, ctx context.Context,
config Config, config Config,
log *zerolog.Logger, log *zerolog.Logger,
@ -153,12 +162,17 @@ func ServeMetrics(
Handler: h, Handler: h,
} }
for _, l := range listeners {
wg.Add(1) wg.Add(1)
go func() { go func(l net.Listener) {
defer wg.Done() defer wg.Done()
err = server.Serve(l) if serveErr := server.Serve(l); serveErr != nil && serveErr != http.ErrServerClosed {
}() log.Err(serveErr).Msgf("Metrics server failed on %s", l.Addr())
}
}(l)
log.Info().Msgf("Starting metrics server on %s", fmt.Sprintf("%v/metrics", l.Addr())) log.Info().Msgf("Starting metrics server on %s", fmt.Sprintf("%v/metrics", l.Addr()))
}
// server.Serve will hang if server.Shutdown is called before the server is // server.Serve will hang if server.Shutdown is called before the server is
// fully started up. So add artificial delay. // fully started up. So add artificial delay.
time.Sleep(startupTime) time.Sleep(startupTime)
@ -174,12 +188,8 @@ func ServeMetrics(
cancel() cancel()
wg.Wait() wg.Wait()
if err == http.ErrServerClosed {
log.Info().Msg("Metrics server stopped") log.Info().Msg("Metrics server stopped")
return nil return nil
}
log.Err(err).Msg("Metrics server failed")
return err
} }
func RegisterBuildInfo(buildType, buildTime, version string) { func RegisterBuildInfo(buildType, buildTime, version string) {

View File

@ -13,28 +13,49 @@ import (
func TestMetricsListenerCreation(t *testing.T) { func TestMetricsListenerCreation(t *testing.T) {
t.Parallel() t.Parallel()
listeners := gracenet.Net{} listeners := gracenet.Net{}
listener1, err := metrics.CreateMetricsListener(&listeners, metrics.GetMetricsDefaultAddress("host")) ls1, err := metrics.CreateMetricsListener(&listeners, []string{metrics.GetMetricsDefaultAddress("host")})
require.NoError(t, err)
require.Len(t, ls1, 1)
listener1 := ls1[0]
assert.Equal(t, "127.0.0.1:20241", listener1.Addr().String()) assert.Equal(t, "127.0.0.1:20241", listener1.Addr().String())
ls2, err := metrics.CreateMetricsListener(&listeners, []string{metrics.GetMetricsDefaultAddress("host")})
require.NoError(t, err) require.NoError(t, err)
listener2, err := metrics.CreateMetricsListener(&listeners, metrics.GetMetricsDefaultAddress("host")) require.Len(t, ls2, 1)
listener2 := ls2[0]
assert.Equal(t, "127.0.0.1:20242", listener2.Addr().String()) assert.Equal(t, "127.0.0.1:20242", listener2.Addr().String())
ls3, err := metrics.CreateMetricsListener(&listeners, []string{metrics.GetMetricsDefaultAddress("host")})
require.NoError(t, err) require.NoError(t, err)
listener3, err := metrics.CreateMetricsListener(&listeners, metrics.GetMetricsDefaultAddress("host")) require.Len(t, ls3, 1)
listener3 := ls3[0]
assert.Equal(t, "127.0.0.1:20243", listener3.Addr().String()) assert.Equal(t, "127.0.0.1:20243", listener3.Addr().String())
ls4, err := metrics.CreateMetricsListener(&listeners, []string{metrics.GetMetricsDefaultAddress("host")})
require.NoError(t, err) require.NoError(t, err)
listener4, err := metrics.CreateMetricsListener(&listeners, metrics.GetMetricsDefaultAddress("host")) require.Len(t, ls4, 1)
listener4 := ls4[0]
assert.Equal(t, "127.0.0.1:20244", listener4.Addr().String()) assert.Equal(t, "127.0.0.1:20244", listener4.Addr().String())
ls5, err := metrics.CreateMetricsListener(&listeners, []string{metrics.GetMetricsDefaultAddress("host")})
require.NoError(t, err) require.NoError(t, err)
listener5, err := metrics.CreateMetricsListener(&listeners, metrics.GetMetricsDefaultAddress("host")) require.Len(t, ls5, 1)
listener5 := ls5[0]
assert.Equal(t, "127.0.0.1:20245", listener5.Addr().String()) assert.Equal(t, "127.0.0.1:20245", listener5.Addr().String())
ls6, err := metrics.CreateMetricsListener(&listeners, []string{metrics.GetMetricsDefaultAddress("host")})
require.NoError(t, err) require.NoError(t, err)
listener6, err := metrics.CreateMetricsListener(&listeners, metrics.GetMetricsDefaultAddress("host")) require.Len(t, ls6, 1)
listener6 := ls6[0]
addresses := [5]string{"127.0.0.1:20241", "127.0.0.1:20242", "127.0.0.1:20243", "127.0.0.1:20244", "127.0.0.1:20245"} addresses := [5]string{"127.0.0.1:20241", "127.0.0.1:20242", "127.0.0.1:20243", "127.0.0.1:20244", "127.0.0.1:20245"}
assert.NotContains(t, addresses, listener6.Addr().String()) assert.NotContains(t, addresses, listener6.Addr().String())
ls7, err := metrics.CreateMetricsListener(&listeners, []string{"localhost:12345"})
require.NoError(t, err) require.NoError(t, err)
listener7, err := metrics.CreateMetricsListener(&listeners, "localhost:12345") require.Len(t, ls7, 1)
listener7 := ls7[0]
assert.Equal(t, "127.0.0.1:12345", listener7.Addr().String()) assert.Equal(t, "127.0.0.1:12345", listener7.Addr().String())
require.NoError(t, err)
err = listener1.Close() err = listener1.Close()
require.NoError(t, err) require.NoError(t, err)
err = listener2.Close() err = listener2.Close()
@ -49,4 +70,14 @@ func TestMetricsListenerCreation(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
err = listener7.Close() err = listener7.Close()
require.NoError(t, err) require.NoError(t, err)
ls8, err := metrics.CreateMetricsListener(&listeners, []string{"127.0.0.1:12346", "127.0.0.1:12347"})
require.NoError(t, err)
require.Len(t, ls8, 2)
assert.Equal(t, "127.0.0.1:12346", ls8[0].Addr().String())
assert.Equal(t, "127.0.0.1:12347", ls8[1].Addr().String())
err = ls8[0].Close()
require.NoError(t, err)
err = ls8[1].Close()
require.NoError(t, err)
} }