@@ -39,6 +39,7 @@ import (
3939 "tailscale.com/types/key"
4040
4141 "cdr.dev/slog/sloggers/slogtest"
42+ "github.com/coder/coder/v2/buildinfo"
4243 "github.com/coder/coder/v2/cli"
4344 "github.com/coder/coder/v2/cli/clitest"
4445 "github.com/coder/coder/v2/cli/config"
@@ -947,36 +948,40 @@ func TestServer(t *testing.T) {
947948 t .Run ("Telemetry" , func (t * testing.T ) {
948949 t .Parallel ()
949950
950- deployment := make (chan struct {}, 64 )
951- snapshot := make (chan * telemetry.Snapshot , 64 )
952- r := chi .NewRouter ()
953- r .Post ("/deployment" , func (w http.ResponseWriter , r * http.Request ) {
954- w .WriteHeader (http .StatusAccepted )
955- deployment <- struct {}{}
956- })
957- r .Post ("/snapshot" , func (w http.ResponseWriter , r * http.Request ) {
958- w .WriteHeader (http .StatusAccepted )
959- ss := & telemetry.Snapshot {}
960- err := json .NewDecoder (r .Body ).Decode (ss )
961- require .NoError (t , err )
962- snapshot <- ss
963- })
964- server := httptest .NewServer (r )
965- defer server .Close ()
951+ telemetryServerURL , deployment , snapshot := mockTelemetryServer (t )
966952
967- inv , _ := clitest .New (t ,
953+ inv , cfg := clitest .New (t ,
968954 "server" ,
969955 "--in-memory" ,
970956 "--http-address" , ":0" ,
971957 "--access-url" , "http://example.com" ,
972958 "--telemetry" ,
973- "--telemetry-url" , server . URL ,
959+ "--telemetry-url" , telemetryServerURL . String () ,
974960 "--cache-dir" , t .TempDir (),
975961 )
976962 clitest .Start (t , inv )
977963
978964 <- deployment
979965 <- snapshot
966+
967+ accessURL := waitAccessURL (t , cfg )
968+
969+ ctx := testutil .Context (t , testutil .WaitMedium )
970+ client := codersdk .New (accessURL )
971+ body , err := client .Request (ctx , http .MethodGet , "/" , nil )
972+ require .NoError (t , err )
973+ require .NoError (t , body .Body .Close ())
974+
975+ require .Eventually (t , func () bool {
976+ snap := <- snapshot
977+ htmlFirstServedFound := false
978+ for _ , item := range snap .TelemetryItems {
979+ if item .Key == string (telemetry .TelemetryItemKeyHTMLFirstServedAt ) {
980+ htmlFirstServedFound = true
981+ }
982+ }
983+ return htmlFirstServedFound
984+ }, testutil .WaitMedium , testutil .IntervalFast , "no html_first_served telemetry item" )
980985 })
981986 t .Run ("Prometheus" , func (t * testing.T ) {
982987 t .Parallel ()
@@ -1990,3 +1995,148 @@ func TestServer_DisabledDERP(t *testing.T) {
19901995 err = c .Connect (ctx )
19911996 require .Error (t , err )
19921997}
1998+
1999+ type runServerOpts struct {
2000+ waitForSnapshot bool
2001+ telemetryDisabled bool
2002+ waitForTelemetryDisabledCheck bool
2003+ }
2004+
2005+ func TestServer_TelemetryDisabled_FinalReport (t * testing.T ) {
2006+ t .Parallel ()
2007+
2008+ if ! dbtestutil .WillUsePostgres () {
2009+ t .Skip ("this test requires postgres" )
2010+ }
2011+
2012+ telemetryServerURL , deployment , snapshot := mockTelemetryServer (t )
2013+ dbConnURL , err := dbtestutil .Open (t )
2014+ require .NoError (t , err )
2015+
2016+ cacheDir := t .TempDir ()
2017+ runServer := func (t * testing.T , opts runServerOpts ) (chan error , context.CancelFunc ) {
2018+ ctx , cancelFunc := context .WithCancel (context .Background ())
2019+ inv , _ := clitest .New (t ,
2020+ "server" ,
2021+ "--postgres-url" , dbConnURL ,
2022+ "--http-address" , ":0" ,
2023+ "--access-url" , "http://example.com" ,
2024+ "--telemetry=" + strconv .FormatBool (! opts .telemetryDisabled ),
2025+ "--telemetry-url" , telemetryServerURL .String (),
2026+ "--cache-dir" , cacheDir ,
2027+ "--log-filter" , ".*" ,
2028+ )
2029+ finished := make (chan bool , 2 )
2030+ errChan := make (chan error , 1 )
2031+ pty := ptytest .New (t ).Attach (inv )
2032+ go func () {
2033+ errChan <- inv .WithContext (ctx ).Run ()
2034+ finished <- true
2035+ }()
2036+ go func () {
2037+ defer func () {
2038+ finished <- true
2039+ }()
2040+ if opts .waitForSnapshot {
2041+ pty .ExpectMatchContext (testutil .Context (t , testutil .WaitLong ), "submitted snapshot" )
2042+ }
2043+ if opts .waitForTelemetryDisabledCheck {
2044+ pty .ExpectMatchContext (testutil .Context (t , testutil .WaitLong ), "finished telemetry status check" )
2045+ }
2046+ }()
2047+ <- finished
2048+ return errChan , cancelFunc
2049+ }
2050+ waitForShutdown := func (t * testing.T , errChan chan error ) error {
2051+ t .Helper ()
2052+ select {
2053+ case err := <- errChan :
2054+ return err
2055+ case <- time .After (testutil .WaitMedium ):
2056+ t .Fatalf ("timed out waiting for server to shutdown" )
2057+ }
2058+ return nil
2059+ }
2060+
2061+ errChan , cancelFunc := runServer (t , runServerOpts {telemetryDisabled : true , waitForTelemetryDisabledCheck : true })
2062+ cancelFunc ()
2063+ require .NoError (t , waitForShutdown (t , errChan ))
2064+
2065+ // Since telemetry was disabled, we expect no deployments or snapshots.
2066+ require .Empty (t , deployment )
2067+ require .Empty (t , snapshot )
2068+
2069+ errChan , cancelFunc = runServer (t , runServerOpts {waitForSnapshot : true })
2070+ cancelFunc ()
2071+ require .NoError (t , waitForShutdown (t , errChan ))
2072+ // we expect to see a deployment and a snapshot twice:
2073+ // 1. the first pair is sent when the server starts
2074+ // 2. the second pair is sent when the server shuts down
2075+ for i := 0 ; i < 2 ; i ++ {
2076+ select {
2077+ case <- snapshot :
2078+ case <- time .After (testutil .WaitShort / 2 ):
2079+ t .Fatalf ("timed out waiting for snapshot" )
2080+ }
2081+ select {
2082+ case <- deployment :
2083+ case <- time .After (testutil .WaitShort / 2 ):
2084+ t .Fatalf ("timed out waiting for deployment" )
2085+ }
2086+ }
2087+
2088+ errChan , cancelFunc = runServer (t , runServerOpts {telemetryDisabled : true , waitForTelemetryDisabledCheck : true })
2089+ cancelFunc ()
2090+ require .NoError (t , waitForShutdown (t , errChan ))
2091+
2092+ // Since telemetry is disabled, we expect no deployment. We expect a snapshot
2093+ // with the telemetry disabled item.
2094+ require .Empty (t , deployment )
2095+ select {
2096+ case ss := <- snapshot :
2097+ require .Len (t , ss .TelemetryItems , 1 )
2098+ require .Equal (t , string (telemetry .TelemetryItemKeyTelemetryEnabled ), ss .TelemetryItems [0 ].Key )
2099+ require .Equal (t , "false" , ss .TelemetryItems [0 ].Value )
2100+ case <- time .After (testutil .WaitShort / 2 ):
2101+ t .Fatalf ("timed out waiting for snapshot" )
2102+ }
2103+
2104+ errChan , cancelFunc = runServer (t , runServerOpts {telemetryDisabled : true , waitForTelemetryDisabledCheck : true })
2105+ cancelFunc ()
2106+ require .NoError (t , waitForShutdown (t , errChan ))
2107+ // Since telemetry is disabled and we've already sent a snapshot, we expect no
2108+ // new deployments or snapshots.
2109+ require .Empty (t , deployment )
2110+ require .Empty (t , snapshot )
2111+ }
2112+
2113+ func mockTelemetryServer (t * testing.T ) (* url.URL , chan * telemetry.Deployment , chan * telemetry.Snapshot ) {
2114+ t .Helper ()
2115+ deployment := make (chan * telemetry.Deployment , 64 )
2116+ snapshot := make (chan * telemetry.Snapshot , 64 )
2117+ r := chi .NewRouter ()
2118+ r .Post ("/deployment" , func (w http.ResponseWriter , r * http.Request ) {
2119+ require .Equal (t , buildinfo .Version (), r .Header .Get (telemetry .VersionHeader ))
2120+ dd := & telemetry.Deployment {}
2121+ err := json .NewDecoder (r .Body ).Decode (dd )
2122+ require .NoError (t , err )
2123+ deployment <- dd
2124+ // Ensure the header is sent only after deployment is sent
2125+ w .WriteHeader (http .StatusAccepted )
2126+ })
2127+ r .Post ("/snapshot" , func (w http.ResponseWriter , r * http.Request ) {
2128+ require .Equal (t , buildinfo .Version (), r .Header .Get (telemetry .VersionHeader ))
2129+ ss := & telemetry.Snapshot {}
2130+ err := json .NewDecoder (r .Body ).Decode (ss )
2131+ require .NoError (t , err )
2132+ snapshot <- ss
2133+ // Ensure the header is sent only after snapshot is sent
2134+ w .WriteHeader (http .StatusAccepted )
2135+ })
2136+ server := httptest .NewServer (r )
2137+ t .Cleanup (server .Close )
2138+ serverURL , err := url .Parse (server .URL )
2139+ require .NoError (t , err )
2140+
2141+ return serverURL , deployment , snapshot
2142+ }
0 commit comments