Skip to content

Commit 35aac55

Browse files
committed
Ensure telemetry is tracked prior to exit
1 parent 9e8b696 commit 35aac55

File tree

4 files changed

+51
-46
lines changed

4 files changed

+51
-46
lines changed

cli/server.go

+3-7
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,6 @@ func server() *cobra.Command {
8383
oauth2GithubClientSecret string
8484
oauth2GithubAllowedOrganizations []string
8585
oauth2GithubAllowSignups bool
86-
telemetryEnable bool
8786
telemetryURL string
8887
tlsCertFile string
8988
tlsClientCAFile string
@@ -312,11 +311,7 @@ func server() *cobra.Command {
312311
if err != nil {
313312
return xerrors.Errorf("parse telemetry url: %w", err)
314313
}
315-
// Disable telemetry if the in-memory database is used unless explicitly defined!
316-
if inMemoryDatabase && !cmd.Flags().Changed("telemetry") {
317-
telemetryEnable = false
318-
}
319-
if telemetryEnable {
314+
if !inMemoryDatabase || cmd.Flags().Changed("telemetry-url") {
320315
options.Telemetry, err = telemetry.New(telemetry.Options{
321316
BuiltinPostgres: builtinPostgres,
322317
DeploymentID: deploymentID,
@@ -487,6 +482,8 @@ func server() *cobra.Command {
487482
<-devTunnelErrChan
488483
}
489484

485+
// Ensures a last report can be sent before exit!
486+
options.Telemetry.Close()
490487
cmd.Println("Waiting for WebSocket connections to close...")
491488
shutdownConns()
492489
coderAPI.Close()
@@ -534,7 +531,6 @@ func server() *cobra.Command {
534531
"Specifies organizations the user must be a member of to authenticate with GitHub.")
535532
cliflag.BoolVarP(root.Flags(), &oauth2GithubAllowSignups, "oauth2-github-allow-signups", "", "CODER_OAUTH2_GITHUB_ALLOW_SIGNUPS", false,
536533
"Specifies whether new users can sign up with GitHub.")
537-
cliflag.BoolVarP(root.Flags(), &telemetryEnable, "telemetry", "", "CODER_TELEMETRY", true, "Specifies whether telemetry is enabled or not. Coder collects anonymized usage data to help improve our product!")
538534
cliflag.StringVarP(root.Flags(), &telemetryURL, "telemetry-url", "", "CODER_TELEMETRY_URL", "https://telemetry.coder.com", "Specifies a URL to send telemetry to.")
539535
_ = root.Flags().MarkHidden("telemetry-url")
540536
cliflag.BoolVarP(root.Flags(), &tlsEnable, "tls-enable", "", "CODER_TLS_ENABLE", false, "Specifies if TLS will be enabled")

cli/server_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,7 @@ func TestServer(t *testing.T) {
259259
server := httptest.NewServer(r)
260260
t.Cleanup(server.Close)
261261

262-
root, _ := clitest.New(t, "server", "--in-memory", "--address", ":0", "--telemetry", "--telemetry-url", server.URL)
262+
root, _ := clitest.New(t, "server", "--in-memory", "--address", ":0", "--telemetry-url", server.URL)
263263
errC := make(chan error)
264264
go func() {
265265
errC <- root.ExecuteContext(ctx)

coderd/provisionerdaemons.go

+17-10
Original file line numberDiff line numberDiff line change
@@ -493,21 +493,28 @@ func (server *provisionerdServer) FailJob(ctx context.Context, failJob *proto.Fa
493493
if job.CompletedAt.Valid {
494494
return nil, xerrors.Errorf("job already completed")
495495
}
496+
job.CompletedAt = sql.NullTime{
497+
Time: database.Now(),
498+
Valid: true,
499+
}
500+
job.Error = sql.NullString{
501+
String: failJob.Error,
502+
Valid: failJob.Error != "",
503+
}
504+
496505
err = server.Database.UpdateProvisionerJobWithCompleteByID(ctx, database.UpdateProvisionerJobWithCompleteByIDParams{
497-
ID: jobID,
498-
CompletedAt: sql.NullTime{
499-
Time: database.Now(),
500-
Valid: true,
501-
},
502-
UpdatedAt: database.Now(),
503-
Error: sql.NullString{
504-
String: failJob.Error,
505-
Valid: failJob.Error != "",
506-
},
506+
ID: jobID,
507+
CompletedAt: job.CompletedAt,
508+
UpdatedAt: database.Now(),
509+
Error: job.Error,
507510
})
508511
if err != nil {
509512
return nil, xerrors.Errorf("update provisioner job: %w", err)
510513
}
514+
server.Telemetry.Report(&telemetry.Snapshot{
515+
ProvisionerJobs: []telemetry.ProvisionerJob{telemetry.ConvertProvisionerJob(job)},
516+
})
517+
511518
switch jobType := failJob.Type.(type) {
512519
case *proto.FailedJob_WorkspaceBuild_:
513520
if jobType.WorkspaceBuild.State == nil {

coderd/telemetry/telemetry.go

+30-28
Original file line numberDiff line numberDiff line change
@@ -106,34 +106,34 @@ type remoteReporter struct {
106106
}
107107

108108
func (r *remoteReporter) Report(snapshot *Snapshot) {
109-
snapshot.DeploymentID = r.options.DeploymentID
109+
go r.reportSync(snapshot)
110+
}
110111

111-
// Runs in a goroutine so it's non-blocking to callers!
112-
go func() {
113-
data, err := json.Marshal(snapshot)
114-
if err != nil {
115-
r.options.Logger.Error(r.ctx, "marshal snapshot: %w", slog.Error(err))
116-
return
117-
}
118-
req, err := http.NewRequestWithContext(r.ctx, "POST", r.snapshotURL.String(), bytes.NewReader(data))
119-
if err != nil {
120-
r.options.Logger.Error(r.ctx, "create request", slog.Error(err))
121-
return
122-
}
123-
req.Header.Set(VersionHeader, buildinfo.Version())
124-
resp, err := http.DefaultClient.Do(req)
125-
if err != nil {
126-
// If the request fails it's not necessarily an error.
127-
// In an airgapped environment, it's fine if this fails!
128-
r.options.Logger.Debug(r.ctx, "submit", slog.Error(err))
129-
return
130-
}
131-
if resp.StatusCode != http.StatusAccepted {
132-
r.options.Logger.Debug(r.ctx, "bad response from telemetry server", slog.F("status", resp.StatusCode))
133-
return
134-
}
135-
r.options.Logger.Debug(r.ctx, "submitted snapshot")
136-
}()
112+
func (r *remoteReporter) reportSync(snapshot *Snapshot) {
113+
snapshot.DeploymentID = r.options.DeploymentID
114+
data, err := json.Marshal(snapshot)
115+
if err != nil {
116+
r.options.Logger.Error(r.ctx, "marshal snapshot: %w", slog.Error(err))
117+
return
118+
}
119+
req, err := http.NewRequestWithContext(r.ctx, "POST", r.snapshotURL.String(), bytes.NewReader(data))
120+
if err != nil {
121+
r.options.Logger.Error(r.ctx, "create request", slog.Error(err))
122+
return
123+
}
124+
req.Header.Set(VersionHeader, buildinfo.Version())
125+
resp, err := http.DefaultClient.Do(req)
126+
if err != nil {
127+
// If the request fails it's not necessarily an error.
128+
// In an airgapped environment, it's fine if this fails!
129+
r.options.Logger.Debug(r.ctx, "submit", slog.Error(err))
130+
return
131+
}
132+
if resp.StatusCode != http.StatusAccepted {
133+
r.options.Logger.Debug(r.ctx, "bad response from telemetry server", slog.F("status", resp.StatusCode))
134+
return
135+
}
136+
r.options.Logger.Debug(r.ctx, "submitted snapshot")
137137
}
138138

139139
func (r *remoteReporter) Close() {
@@ -203,7 +203,7 @@ func (r *remoteReporter) reportWithDeployment() {
203203
r.options.Logger.Error(r.ctx, "create snapshot", slog.Error(err))
204204
return
205205
}
206-
r.Report(snapshot)
206+
r.reportSync(snapshot)
207207
}
208208

209209
// deployment collects host information and reports it to the telemetry server.
@@ -446,6 +446,7 @@ func ConvertWorkspace(workspace database.Workspace) Workspace {
446446
TemplateID: workspace.TemplateID,
447447
CreatedAt: workspace.CreatedAt,
448448
Deleted: workspace.Deleted,
449+
Name: workspace.Name,
449450
AutostartSchedule: workspace.AutostartSchedule.String,
450451
}
451452
}
@@ -670,6 +671,7 @@ type Workspace struct {
670671
TemplateID uuid.UUID `json:"template_id"`
671672
CreatedAt time.Time `json:"created_at"`
672673
Deleted bool `json:"deleted"`
674+
Name string `json:"name"`
673675
AutostartSchedule string `json:"autostart_schedule"`
674676
}
675677

0 commit comments

Comments
 (0)