feat: support multiple metrics addresses via repeated flag
This commit is contained in:
parent
a0bcbf6a44
commit
81caea30f0
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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,13 +188,9 @@ 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) {
|
||||||
buildInfo := prometheus.NewGaugeVec(
|
buildInfo := prometheus.NewGaugeVec(
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue