Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 63246e8

Browse files
committed
track the first time html is served in telemetry
1 parent 3897ea4 commit 63246e8

File tree

12 files changed

+220
-20
lines changed

12 files changed

+220
-20
lines changed

coderd/coderd.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -585,6 +585,8 @@ func New(options *Options) *API {
585585
AppearanceFetcher: &api.AppearanceFetcher,
586586
BuildInfo: buildInfo,
587587
Entitlements: options.Entitlements,
588+
Telemetry: options.Telemetry,
589+
Logger: options.Logger.Named("site"),
588590
})
589591
api.SiteHandler.Experiments.Store(&experiments)
590592

coderd/database/dbauthz/dbauthz.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2096,6 +2096,13 @@ func (q *querier) GetTailnetTunnelPeerIDs(ctx context.Context, srcID uuid.UUID)
20962096
return q.db.GetTailnetTunnelPeerIDs(ctx, srcID)
20972097
}
20982098

2099+
func (q *querier) GetTelemetryHTMLFirstServedAt(ctx context.Context) (string, error) {
2100+
if err := q.authorizeContext(ctx, policy.ActionRead, rbac.ResourceSystem); err != nil {
2101+
return "", err
2102+
}
2103+
return q.db.GetTelemetryHTMLFirstServedAt(ctx)
2104+
}
2105+
20992106
func (q *querier) GetTemplateAppInsights(ctx context.Context, arg database.GetTemplateAppInsightsParams) ([]database.GetTemplateAppInsightsRow, error) {
21002107
if err := q.authorizeTemplateInsights(ctx, arg.TemplateIDs); err != nil {
21012108
return nil, err
@@ -3428,6 +3435,13 @@ func (q *querier) RevokeDBCryptKey(ctx context.Context, activeKeyDigest string)
34283435
return q.db.RevokeDBCryptKey(ctx, activeKeyDigest)
34293436
}
34303437

3438+
func (q *querier) SetTelemetryHTMLFirstServedAt(ctx context.Context, value string) error {
3439+
if err := q.authorizeContext(ctx, policy.ActionUpdate, rbac.ResourceSystem); err != nil {
3440+
return err
3441+
}
3442+
return q.db.SetTelemetryHTMLFirstServedAt(ctx, value)
3443+
}
3444+
34313445
func (q *querier) TryAcquireLock(ctx context.Context, id int64) (bool, error) {
34323446
return q.db.TryAcquireLock(ctx, id)
34333447
}

coderd/database/dbauthz/dbauthz_test.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4224,6 +4224,12 @@ func (s *MethodTestSuite) TestSystemFunctions() {
42244224
s.Run("GetWorkspaceModulesCreatedAfter", s.Subtest(func(db database.Store, check *expects) {
42254225
check.Args(dbtime.Now()).Asserts(rbac.ResourceSystem, policy.ActionRead)
42264226
}))
4227+
s.Run("GetTelemetryHTMLFirstServedAt", s.Subtest(func(db database.Store, check *expects) {
4228+
check.Args().Asserts(rbac.ResourceSystem, policy.ActionRead).Errors(sql.ErrNoRows)
4229+
}))
4230+
s.Run("SetTelemetryHTMLFirstServedAt", s.Subtest(func(db database.Store, check *expects) {
4231+
check.Args(time.Now().Format(time.RFC3339)).Asserts(rbac.ResourceSystem, policy.ActionUpdate)
4232+
}))
42274233
}
42284234

42294235
func (s *MethodTestSuite) TestNotifications() {

coderd/database/dbmem/dbmem.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,7 @@ type data struct {
258258
defaultProxyDisplayName string
259259
defaultProxyIconURL string
260260
userStatusChanges []database.UserStatusChange
261+
htmlFirstServedAt string
261262
}
262263

263264
func tryPercentile(fs []float64, p float64) float64 {
@@ -4330,6 +4331,16 @@ func (*FakeQuerier) GetTailnetTunnelPeerIDs(context.Context, uuid.UUID) ([]datab
43304331
return nil, ErrUnimplemented
43314332
}
43324333

4334+
func (q *FakeQuerier) GetTelemetryHTMLFirstServedAt(ctx context.Context) (string, error) {
4335+
q.mutex.RLock()
4336+
defer q.mutex.RUnlock()
4337+
if q.htmlFirstServedAt == "" {
4338+
return "", sql.ErrNoRows
4339+
}
4340+
4341+
return q.htmlFirstServedAt, nil
4342+
}
4343+
43334344
func (q *FakeQuerier) GetTemplateAppInsights(ctx context.Context, arg database.GetTemplateAppInsightsParams) ([]database.GetTemplateAppInsightsRow, error) {
43344345
err := validateDatabaseType(arg)
43354346
if err != nil {
@@ -9123,6 +9134,16 @@ func (q *FakeQuerier) RevokeDBCryptKey(_ context.Context, activeKeyDigest string
91239134
return sql.ErrNoRows
91249135
}
91259136

9137+
func (q *FakeQuerier) SetTelemetryHTMLFirstServedAt(ctx context.Context, value string) error {
9138+
q.mutex.Lock()
9139+
defer q.mutex.Unlock()
9140+
if q.htmlFirstServedAt != "" {
9141+
return nil
9142+
}
9143+
q.htmlFirstServedAt = value
9144+
return nil
9145+
}
9146+
91269147
func (*FakeQuerier) TryAcquireLock(_ context.Context, _ int64) (bool, error) {
91279148
return false, xerrors.New("TryAcquireLock must only be called within a transaction")
91289149
}

coderd/database/dbmetrics/querymetrics.go

Lines changed: 14 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/database/dbmock/dbmock.go

Lines changed: 29 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/database/querier.go

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/database/queries.sql.go

Lines changed: 22 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/database/queries/siteconfig.sql

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,3 +107,10 @@ ON CONFLICT (key) DO UPDATE SET value = $2 WHERE site_configs.key = $1;
107107
DELETE FROM site_configs
108108
WHERE site_configs.key = $1;
109109

110+
-- name: SetTelemetryHTMLFirstServedAt :exec
111+
INSERT INTO site_configs (key, value)
112+
VALUES ('telemetry_html_first_served_at', $1)
113+
ON CONFLICT (key) DO NOTHING;
114+
115+
-- name: GetTelemetryHTMLFirstServedAt :one
116+
SELECT value FROM site_configs WHERE key = 'telemetry_html_first_served_at';

coderd/telemetry/telemetry.go

Lines changed: 48 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,8 @@ type Reporter interface {
9999
// database. For example, if a new user is added, a snapshot can
100100
// contain just that user entry.
101101
Report(snapshot *Snapshot)
102+
// ReportDeployment sends deployment information to the telemetry server.
103+
ReportDeployment()
102104
Enabled() bool
103105
Close()
104106
}
@@ -222,6 +224,12 @@ func (r *remoteReporter) reportWithDeployment() {
222224
r.reportSync(snapshot)
223225
}
224226

227+
func (r *remoteReporter) ReportDeployment() {
228+
if err := r.deployment(); err != nil {
229+
r.options.Logger.Debug(r.ctx, "failed to report deployment", slog.Error(err))
230+
}
231+
}
232+
225233
// deployment collects host information and reports it to the telemetry server.
226234
func (r *remoteReporter) deployment() error {
227235
sysInfoHost, err := sysinfo.Host()
@@ -250,26 +258,32 @@ func (r *remoteReporter) deployment() error {
250258
r.options.Logger.Debug(r.ctx, "check IDP org sync", slog.Error(err))
251259
}
252260

261+
htmlFirstServedAt, err := getHTMLFirstServedAt(r.ctx, r.options.Database)
262+
if err != nil && !errors.Is(err, sql.ErrNoRows) {
263+
r.options.Logger.Debug(r.ctx, "get telemetry html first served at", slog.Error(err))
264+
}
265+
253266
data, err := json.Marshal(&Deployment{
254-
ID: r.options.DeploymentID,
255-
Architecture: sysInfo.Architecture,
256-
BuiltinPostgres: r.options.BuiltinPostgres,
257-
Containerized: containerized,
258-
Config: r.options.DeploymentConfig,
259-
Kubernetes: os.Getenv("KUBERNETES_SERVICE_HOST") != "",
260-
InstallSource: installSource,
261-
Tunnel: r.options.Tunnel,
262-
OSType: sysInfo.OS.Type,
263-
OSFamily: sysInfo.OS.Family,
264-
OSPlatform: sysInfo.OS.Platform,
265-
OSName: sysInfo.OS.Name,
266-
OSVersion: sysInfo.OS.Version,
267-
CPUCores: runtime.NumCPU(),
268-
MemoryTotal: mem.Total,
269-
MachineID: sysInfo.UniqueID,
270-
StartedAt: r.startedAt,
271-
ShutdownAt: r.shutdownAt,
272-
IDPOrgSync: &idpOrgSync,
267+
ID: r.options.DeploymentID,
268+
Architecture: sysInfo.Architecture,
269+
BuiltinPostgres: r.options.BuiltinPostgres,
270+
Containerized: containerized,
271+
Config: r.options.DeploymentConfig,
272+
Kubernetes: os.Getenv("KUBERNETES_SERVICE_HOST") != "",
273+
InstallSource: installSource,
274+
Tunnel: r.options.Tunnel,
275+
OSType: sysInfo.OS.Type,
276+
OSFamily: sysInfo.OS.Family,
277+
OSPlatform: sysInfo.OS.Platform,
278+
OSName: sysInfo.OS.Name,
279+
OSVersion: sysInfo.OS.Version,
280+
CPUCores: runtime.NumCPU(),
281+
MemoryTotal: mem.Total,
282+
MachineID: sysInfo.UniqueID,
283+
StartedAt: r.startedAt,
284+
ShutdownAt: r.shutdownAt,
285+
IDPOrgSync: &idpOrgSync,
286+
HTMLFirstServedAt: htmlFirstServedAt,
273287
})
274288
if err != nil {
275289
return xerrors.Errorf("marshal deployment: %w", err)
@@ -330,6 +344,18 @@ func checkIDPOrgSync(ctx context.Context, db database.Store, values *codersdk.De
330344
return syncConfig.Field != "", nil
331345
}
332346

347+
func getHTMLFirstServedAt(ctx context.Context, db database.Store) (*time.Time, error) {
348+
htmlFirstServedAtStr, err := db.GetTelemetryHTMLFirstServedAt(ctx)
349+
if err != nil {
350+
return nil, xerrors.Errorf("get telemetry html first served at: %w", err)
351+
}
352+
t, err := time.Parse(time.RFC3339, htmlFirstServedAtStr)
353+
if err != nil {
354+
return nil, xerrors.Errorf("parse telemetry html first served at: %w", err)
355+
}
356+
return &t, nil
357+
}
358+
333359
// createSnapshot collects a full snapshot from the database.
334360
func (r *remoteReporter) createSnapshot() (*Snapshot, error) {
335361
var (
@@ -1036,7 +1062,8 @@ type Deployment struct {
10361062
ShutdownAt *time.Time `json:"shutdown_at"`
10371063
// While IDPOrgSync will always be set, it's nullable to make
10381064
// the struct backwards compatible with older coder versions.
1039-
IDPOrgSync *bool `json:"idp_org_sync"`
1065+
IDPOrgSync *bool `json:"idp_org_sync"`
1066+
HTMLFirstServedAt *time.Time `json:"html_first_served_at"`
10401067
}
10411068

10421069
type APIKey struct {
@@ -1541,3 +1568,4 @@ type noopReporter struct{}
15411568
func (*noopReporter) Report(_ *Snapshot) {}
15421569
func (*noopReporter) Enabled() bool { return false }
15431570
func (*noopReporter) Close() {}
1571+
func (*noopReporter) ReportDeployment() {}

coderd/telemetry/telemetry_test.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,25 @@ func TestTelemetry(t *testing.T) {
306306
deployment, _ = collectSnapshot(t, db, nil)
307307
require.True(t, *deployment.IDPOrgSync)
308308
})
309+
t.Run("HTMLFirstServedAt", func(t *testing.T) {
310+
t.Parallel()
311+
db, _ := dbtestutil.NewDB(t)
312+
deployment, _ := collectSnapshot(t, db, nil)
313+
require.Nil(t, deployment.HTMLFirstServedAt)
314+
315+
ctx := testutil.Context(t, testutil.WaitMedium)
316+
now := time.Now().Format(time.RFC3339)
317+
parsedNow, err := time.Parse(time.RFC3339, now)
318+
require.NoError(t, err)
319+
require.NoError(t, db.SetTelemetryHTMLFirstServedAt(ctx, now))
320+
deployment, _ = collectSnapshot(t, db, nil)
321+
require.Equal(t, *deployment.HTMLFirstServedAt, parsedNow)
322+
323+
// Test idempotency
324+
require.NoError(t, db.SetTelemetryHTMLFirstServedAt(ctx, time.Now().Add(time.Hour).Format(time.RFC3339)))
325+
deployment, _ = collectSnapshot(t, db, nil)
326+
require.Equal(t, *deployment.HTMLFirstServedAt, parsedNow)
327+
})
309328
}
310329

311330
// nolint:paralleltest

0 commit comments

Comments
 (0)