Skip to content

Commit db9b135

Browse files
committed
ReportDisabledIfNeeded
1 parent ff86e66 commit db9b135

File tree

3 files changed

+84
-6
lines changed

3 files changed

+84
-6
lines changed

cli/server.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -814,11 +814,31 @@ func (r *RootCmd) Server(newAPI func(context.Context, *coderd.Options) (*coderd.
814814
if err != nil {
815815
return xerrors.Errorf("create telemetry reporter: %w", err)
816816
}
817+
go options.Telemetry.RunSnapshotter()
817818
defer options.Telemetry.Close()
818819
} else {
819820
logger.Warn(ctx, fmt.Sprintf(`telemetry disabled, unable to notify of security issues. Read more: %s/admin/setup/telemetry`, vals.DocsURL.String()))
820821
}
821822

823+
if !vals.Telemetry.Enable.Value() {
824+
go func() {
825+
reporter, err := telemetry.New(telemetry.Options{
826+
DeploymentID: deploymentID,
827+
Database: options.Database,
828+
Logger: logger.Named("telemetry"),
829+
URL: vals.Telemetry.URL.Value(),
830+
})
831+
if err != nil {
832+
logger.Debug(ctx, "create telemetry reporter (disabled)", slog.Error(err))
833+
return
834+
}
835+
defer reporter.Close()
836+
if err := reporter.ReportDisabledIfNeeded(); err != nil {
837+
logger.Debug(ctx, "failed to report disabled telemetry", slog.Error(err))
838+
}
839+
}()
840+
}
841+
822842
// This prevents the pprof import from being accidentally deleted.
823843
_ = pprof.Handler
824844
if vals.Pprof.Enable {

coderd/telemetry/telemetry.go

Lines changed: 63 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,9 @@ type Options struct {
5959
// New constructs a reporter for telemetry data.
6060
// Duplicate data will be sent, it's on the server-side to index by UUID.
6161
// Data is anonymized prior to being sent!
62+
//
63+
// The returned Reporter should be started with RunSnapshotter() to begin
64+
// reporting.
6265
func New(options Options) (Reporter, error) {
6366
if options.SnapshotFrequency == 0 {
6467
// Report once every 30mins by default!
@@ -83,7 +86,6 @@ func New(options Options) (Reporter, error) {
8386
snapshotURL: snapshotURL,
8487
startedAt: dbtime.Now(),
8588
}
86-
go reporter.runSnapshotter()
8789
return reporter, nil
8890
}
8991

@@ -101,6 +103,12 @@ type Reporter interface {
101103
Report(snapshot *Snapshot)
102104
Enabled() bool
103105
Close()
106+
// RunSnapshotter runs reporting in a loop. It should be called in a
107+
// goroutine to avoid blocking the caller.
108+
RunSnapshotter()
109+
// ReportDisabledIfNeeded reports disabled telemetry if there was at least one report sent
110+
// before the telemetry was disabled, and we haven't sent a report since the telemetry was disabled.
111+
ReportDisabledIfNeeded() error
104112
}
105113

106114
type remoteReporter struct {
@@ -149,6 +157,12 @@ func (r *remoteReporter) reportSync(snapshot *Snapshot) {
149157
r.options.Logger.Debug(r.ctx, "bad response from telemetry server", slog.F("status", resp.StatusCode))
150158
return
151159
}
160+
if err := r.options.Database.UpsertTelemetryItem(r.ctx, database.UpsertTelemetryItemParams{
161+
Key: string(TelemetryItemKeyLastTelemetryUpdate),
162+
Value: dbtime.Now().Format(time.RFC3339),
163+
}); err != nil {
164+
r.options.Logger.Debug(r.ctx, "upsert last telemetry update", slog.Error(err))
165+
}
152166
r.options.Logger.Debug(r.ctx, "submitted snapshot")
153167
}
154168

@@ -177,7 +191,7 @@ func (r *remoteReporter) isClosed() bool {
177191
}
178192
}
179193

180-
func (r *remoteReporter) runSnapshotter() {
194+
func (r *remoteReporter) RunSnapshotter() {
181195
first := true
182196
ticker := time.NewTicker(r.options.SnapshotFrequency)
183197
defer ticker.Stop()
@@ -330,6 +344,45 @@ func checkIDPOrgSync(ctx context.Context, db database.Store, values *codersdk.De
330344
return syncConfig.Field != "", nil
331345
}
332346

347+
func (r *remoteReporter) ReportDisabledIfNeeded() error {
348+
db := r.options.Database
349+
lastTelemetryUpdate, telemetryUpdateErr := db.GetTelemetryItem(r.ctx, string(TelemetryItemKeyLastTelemetryUpdate))
350+
if telemetryUpdateErr != nil {
351+
r.options.Logger.Debug(r.ctx, "get last telemetry update at", slog.Error(telemetryUpdateErr))
352+
}
353+
telemetryDisabled, telemetryDisabledErr := db.GetTelemetryItem(r.ctx, string(TelemetryItemKeyTelemetryDisabled))
354+
if telemetryDisabledErr != nil {
355+
r.options.Logger.Debug(r.ctx, "get telemetry disabled", slog.Error(telemetryDisabledErr))
356+
}
357+
shouldReportDisabledTelemetry :=
358+
telemetryUpdateErr == nil &&
359+
((telemetryDisabledErr == nil && lastTelemetryUpdate.UpdatedAt.Before(telemetryDisabled.UpdatedAt)) ||
360+
errors.Is(telemetryDisabledErr, sql.ErrNoRows))
361+
if !shouldReportDisabledTelemetry {
362+
return nil
363+
}
364+
365+
if err := db.UpsertTelemetryItem(r.ctx, database.UpsertTelemetryItemParams{
366+
Key: string(TelemetryItemKeyTelemetryDisabled),
367+
Value: time.Now().Format(time.RFC3339),
368+
}); err != nil {
369+
return xerrors.Errorf("upsert telemetry disabled: %w", err)
370+
}
371+
item, err := db.GetTelemetryItem(r.ctx, string(TelemetryItemKeyTelemetryDisabled))
372+
if err != nil {
373+
return xerrors.Errorf("get telemetry disabled: %w", err)
374+
}
375+
376+
r.reportSync(
377+
&Snapshot{
378+
TelemetryItems: []TelemetryItem{
379+
ConvertTelemetryItem(item),
380+
},
381+
},
382+
)
383+
return nil
384+
}
385+
333386
// createSnapshot collects a full snapshot from the database.
334387
func (r *remoteReporter) createSnapshot() (*Snapshot, error) {
335388
var (
@@ -1561,7 +1614,9 @@ type Organization struct {
15611614
type TelemetryItemKey string
15621615

15631616
const (
1564-
TelemetryItemKeyHTMLFirstServedAt TelemetryItemKey = "html_first_served_at"
1617+
TelemetryItemKeyHTMLFirstServedAt TelemetryItemKey = "html_first_served_at"
1618+
TelemetryItemKeyLastTelemetryUpdate TelemetryItemKey = "last_telemetry_update"
1619+
TelemetryItemKeyTelemetryDisabled TelemetryItemKey = "telemetry_disabled"
15651620
)
15661621

15671622
type TelemetryItem struct {
@@ -1573,6 +1628,8 @@ type TelemetryItem struct {
15731628

15741629
type noopReporter struct{}
15751630

1576-
func (*noopReporter) Report(_ *Snapshot) {}
1577-
func (*noopReporter) Enabled() bool { return false }
1578-
func (*noopReporter) Close() {}
1631+
func (*noopReporter) Report(_ *Snapshot) {}
1632+
func (*noopReporter) Enabled() bool { return false }
1633+
func (*noopReporter) Close() {}
1634+
func (*noopReporter) RunSnapshotter() {}
1635+
func (*noopReporter) ReportDisabledIfNeeded() error { return nil }

coderd/telemetry/telemetry_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -398,6 +398,7 @@ func collectSnapshot(t *testing.T, db database.Store, addOptionsFn func(opts tel
398398

399399
reporter, err := telemetry.New(options)
400400
require.NoError(t, err)
401+
go reporter.RunSnapshotter()
401402
t.Cleanup(reporter.Close)
402403
return <-deployment, <-snapshot
403404
}

0 commit comments

Comments
 (0)