@@ -23,9 +23,11 @@ import (
23
23
"crypto/tls"
24
24
"errors"
25
25
"fmt"
26
+ "io"
26
27
"log/slog"
27
28
"net"
28
29
"net/http"
30
+ "net/url"
29
31
"os"
30
32
"path/filepath"
31
33
"strings"
@@ -39,7 +41,9 @@ import (
39
41
"github.com/google/uuid"
40
42
"github.com/gravitational/trace"
41
43
"github.com/jonboulle/clockwork"
44
+ "github.com/prometheus/client_golang/prometheus"
42
45
"github.com/sirupsen/logrus"
46
+ "github.com/stretchr/testify/assert"
43
47
"github.com/stretchr/testify/require"
44
48
"golang.org/x/sync/errgroup"
45
49
"google.golang.org/grpc"
@@ -1849,6 +1853,152 @@ func TestInitDatabaseService(t *testing.T) {
1849
1853
}
1850
1854
}
1851
1855
1856
+ // TestMetricsService tests that the optional metrics service exposes
1857
+ // metrics from both the in-process and global metrics registry. When the
1858
+ // service is disabled, metrics are served by the diagnostics service
1859
+ // (tested in TestMetricsInDiagnosticsService).
1860
+ func TestMetricsService (t * testing.T ) {
1861
+ t .Parallel ()
1862
+ // Test setup: create a listener for the metrics server, get its file descriptor.
1863
+
1864
+ // Note: this code is copied from integrations/helpers/NewListenerOn() to avoid including helpers in a production
1865
+ // build and avoid a cyclic dependency.
1866
+ metricsListener , err := net .Listen ("tcp" , "127.0.0.1:0" )
1867
+ require .NoError (t , err )
1868
+ t .Cleanup (func () {
1869
+ assert .NoError (t , metricsListener .Close ())
1870
+ })
1871
+ require .IsType (t , & net.TCPListener {}, metricsListener )
1872
+ metricsListenerFile , err := metricsListener .(* net.TCPListener ).File ()
1873
+ require .NoError (t , err )
1874
+
1875
+ // Test setup: create a new teleport process
1876
+ dataDir := makeTempDir (t )
1877
+ cfg := servicecfg .MakeDefaultConfig ()
1878
+ cfg .DataDir = dataDir
1879
+ cfg .SetAuthServerAddress (utils.NetAddr {AddrNetwork : "tcp" , Addr : "127.0.0.1:0" })
1880
+ cfg .Auth .Enabled = true
1881
+ cfg .Proxy .Enabled = false
1882
+ cfg .SSH .Enabled = false
1883
+ cfg .DebugService .Enabled = false
1884
+ cfg .Auth .StorageConfig .Params ["path" ] = dataDir
1885
+ cfg .Auth .ListenAddr = utils.NetAddr {AddrNetwork : "tcp" , Addr : "127.0.0.1:0" }
1886
+ cfg .Metrics .Enabled = true
1887
+
1888
+ // Configure the metrics server to use the listener we previously created.
1889
+ cfg .Metrics .ListenAddr = & utils.NetAddr {AddrNetwork : "tcp" , Addr : metricsListener .Addr ().String ()}
1890
+ cfg .FileDescriptors = []* servicecfg.FileDescriptor {
1891
+ {Type : string (ListenerMetrics ), Address : metricsListener .Addr ().String (), File : metricsListenerFile },
1892
+ }
1893
+
1894
+ // Create and start the Teleport service.
1895
+ process , err := NewTeleport (cfg )
1896
+ require .NoError (t , err )
1897
+ require .NoError (t , process .Start ())
1898
+ t .Cleanup (func () {
1899
+ assert .NoError (t , process .Close ())
1900
+ assert .NoError (t , process .Wait ())
1901
+ })
1902
+
1903
+ // Test setup: create our test metrics.
1904
+ nonce := strings .ReplaceAll (uuid .NewString (), "-" , "" )
1905
+ localMetric := prometheus .NewGauge (prometheus.GaugeOpts {
1906
+ Namespace : "test" ,
1907
+ Name : "local_metric_" + nonce ,
1908
+ })
1909
+ globalMetric := prometheus .NewGauge (prometheus.GaugeOpts {
1910
+ Namespace : "test" ,
1911
+ Name : "global_metric_" + nonce ,
1912
+ })
1913
+ require .NoError (t , process .metricsRegistry .Register (localMetric ))
1914
+ require .NoError (t , prometheus .Register (globalMetric ))
1915
+
1916
+ ctx , cancel := context .WithTimeout (context .Background (), 20 * time .Second )
1917
+ t .Cleanup (cancel )
1918
+ _ , err = process .WaitForEvent (ctx , MetricsReady )
1919
+ require .NoError (t , err )
1920
+
1921
+ // Test execution: get metrics and check the tests metrics are here.
1922
+ metricsURL , err := url .Parse ("http://" + metricsListener .Addr ().String ())
1923
+ require .NoError (t , err )
1924
+ metricsURL .Path = "/metrics"
1925
+ resp , err := http .Get (metricsURL .String ())
1926
+ require .NoError (t , err )
1927
+ require .Equal (t , http .StatusOK , resp .StatusCode )
1928
+
1929
+ body , err := io .ReadAll (resp .Body )
1930
+ require .NoError (t , err )
1931
+ require .NoError (t , resp .Body .Close ())
1932
+
1933
+ // Test validation: check that the metrics server served both the local and global registry.
1934
+ require .Contains (t , string (body ), "local_metric_" + nonce )
1935
+ require .Contains (t , string (body ), "global_metric_" + nonce )
1936
+ }
1937
+
1938
+ // TestMetricsInDiagnosticsService tests that the diagnostics service exposes
1939
+ // metrics from both the in-process and global metrics registry when the metrics
1940
+ // service is disabled.
1941
+ func TestMetricsInDiagnosticsService (t * testing.T ) {
1942
+ t .Parallel ()
1943
+ // Test setup: create a new teleport process
1944
+ dataDir := makeTempDir (t )
1945
+ cfg := servicecfg .MakeDefaultConfig ()
1946
+ cfg .DataDir = dataDir
1947
+ cfg .SetAuthServerAddress (utils.NetAddr {AddrNetwork : "tcp" , Addr : "127.0.0.1:0" })
1948
+ cfg .Auth .Enabled = true
1949
+ cfg .Proxy .Enabled = false
1950
+ cfg .SSH .Enabled = false
1951
+ cfg .DebugService .Enabled = false
1952
+ cfg .Auth .StorageConfig .Params ["path" ] = dataDir
1953
+ cfg .Auth .ListenAddr = utils.NetAddr {AddrNetwork : "tcp" , Addr : "127.0.0.1:0" }
1954
+ cfg .DiagnosticAddr = utils.NetAddr {AddrNetwork : "tcp" , Addr : "127.0.0.1:0" }
1955
+
1956
+ // Test setup: Create and start the Teleport service.
1957
+ process , err := NewTeleport (cfg )
1958
+ require .NoError (t , err )
1959
+ require .NoError (t , process .Start ())
1960
+ t .Cleanup (func () {
1961
+ assert .NoError (t , process .Close ())
1962
+ assert .NoError (t , process .Wait ())
1963
+ })
1964
+
1965
+ // Test setup: create our test metrics.
1966
+ nonce := strings .ReplaceAll (uuid .NewString (), "-" , "" )
1967
+ localMetric := prometheus .NewGauge (prometheus.GaugeOpts {
1968
+ Namespace : "test" ,
1969
+ Name : "local_metric_" + nonce ,
1970
+ })
1971
+ globalMetric := prometheus .NewGauge (prometheus.GaugeOpts {
1972
+ Namespace : "test" ,
1973
+ Name : "global_metric_" + nonce ,
1974
+ })
1975
+ require .NoError (t , process .metricsRegistry .Register (localMetric ))
1976
+ require .NoError (t , prometheus .Register (globalMetric ))
1977
+
1978
+ ctx , cancel := context .WithTimeout (context .Background (), 20 * time .Second )
1979
+ t .Cleanup (cancel )
1980
+ _ , err = process .WaitForEvent (ctx , TeleportReadyEvent )
1981
+ require .NoError (t , err )
1982
+
1983
+ // Test execution: query the metrics endpoint and check the tests metrics are here.
1984
+ diagAddr , err := process .DiagnosticAddr ()
1985
+ require .NoError (t , err )
1986
+ metricsURL , err := url .Parse ("http://" + diagAddr .String ())
1987
+ require .NoError (t , err )
1988
+ metricsURL .Path = "/metrics"
1989
+ resp , err := http .Get (metricsURL .String ())
1990
+ require .NoError (t , err )
1991
+ require .Equal (t , http .StatusOK , resp .StatusCode )
1992
+
1993
+ body , err := io .ReadAll (resp .Body )
1994
+ require .NoError (t , err )
1995
+ require .NoError (t , resp .Body .Close ())
1996
+
1997
+ // Test validation: check that the metrics server served both the local and global registry.
1998
+ require .Contains (t , string (body ), "local_metric_" + nonce )
1999
+ require .Contains (t , string (body ), "global_metric_" + nonce )
2000
+ }
2001
+
1852
2002
// makeTempDir makes a temp dir with a shorter name than t.TempDir() in order to
1853
2003
// avoid https://github.com/golang/go/issues/62614.
1854
2004
func makeTempDir (t * testing.T ) string {
0 commit comments