Skip to content

Commit a9775fa

Browse files
stirbyhugodutkaDanielleMaywoodEdwardAngertaslilac
authored
chore: cherry-pick items for 2.19 (coder#16412)
Co-authored-by: Hugo Dutka <hugo@coder.com> Co-authored-by: Danielle Maywood <danielle@themaywoods.com> Co-authored-by: Edward Angert <EdwardAngert@users.noreply.github.com> Co-authored-by: ケイラ <mckayla@hey.com>
1 parent a658ccf commit a9775fa

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

57 files changed

+3876
-347
lines changed

cli/server.go

Lines changed: 35 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -781,40 +781,42 @@ func (r *RootCmd) Server(newAPI func(context.Context, *coderd.Options) (*coderd.
781781
// This should be output before the logs start streaming.
782782
cliui.Infof(inv.Stdout, "\n==> Logs will stream in below (press ctrl+c to gracefully exit):")
783783

784-
if vals.Telemetry.Enable {
785-
vals, err := vals.WithoutSecrets()
786-
if err != nil {
787-
return xerrors.Errorf("remove secrets from deployment values: %w", err)
788-
}
789-
options.Telemetry, err = telemetry.New(telemetry.Options{
790-
BuiltinPostgres: builtinPostgres,
791-
DeploymentID: deploymentID,
792-
Database: options.Database,
793-
Logger: logger.Named("telemetry"),
794-
URL: vals.Telemetry.URL.Value(),
795-
Tunnel: tunnel != nil,
796-
DeploymentConfig: vals,
797-
ParseLicenseJWT: func(lic *telemetry.License) error {
798-
// This will be nil when running in AGPL-only mode.
799-
if options.ParseLicenseClaims == nil {
800-
return nil
801-
}
802-
803-
email, trial, err := options.ParseLicenseClaims(lic.JWT)
804-
if err != nil {
805-
return err
806-
}
807-
if email != "" {
808-
lic.Email = &email
809-
}
810-
lic.Trial = &trial
784+
deploymentConfigWithoutSecrets, err := vals.WithoutSecrets()
785+
if err != nil {
786+
return xerrors.Errorf("remove secrets from deployment values: %w", err)
787+
}
788+
telemetryReporter, err := telemetry.New(telemetry.Options{
789+
Disabled: !vals.Telemetry.Enable.Value(),
790+
BuiltinPostgres: builtinPostgres,
791+
DeploymentID: deploymentID,
792+
Database: options.Database,
793+
Logger: logger.Named("telemetry"),
794+
URL: vals.Telemetry.URL.Value(),
795+
Tunnel: tunnel != nil,
796+
DeploymentConfig: deploymentConfigWithoutSecrets,
797+
ParseLicenseJWT: func(lic *telemetry.License) error {
798+
// This will be nil when running in AGPL-only mode.
799+
if options.ParseLicenseClaims == nil {
811800
return nil
812-
},
813-
})
814-
if err != nil {
815-
return xerrors.Errorf("create telemetry reporter: %w", err)
816-
}
817-
defer options.Telemetry.Close()
801+
}
802+
803+
email, trial, err := options.ParseLicenseClaims(lic.JWT)
804+
if err != nil {
805+
return err
806+
}
807+
if email != "" {
808+
lic.Email = &email
809+
}
810+
lic.Trial = &trial
811+
return nil
812+
},
813+
})
814+
if err != nil {
815+
return xerrors.Errorf("create telemetry reporter: %w", err)
816+
}
817+
defer telemetryReporter.Close()
818+
if vals.Telemetry.Enable.Value() {
819+
options.Telemetry = telemetryReporter
818820
} else {
819821
logger.Warn(ctx, fmt.Sprintf(`telemetry disabled, unable to notify of security issues. Read more: %s/admin/setup/telemetry`, vals.DocsURL.String()))
820822
}

cli/server_test.go

Lines changed: 168 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)