feat(coderd): add enabled experiments to telemetry (#12656)

This commit is contained in:
Danny Kopping 2024-03-19 11:05:29 +02:00 committed by GitHub
parent f0f9569d51
commit ab95ae827d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 45 additions and 7 deletions

View File

@ -11,9 +11,10 @@ import (
"testing" "testing"
"time" "time"
"github.com/coder/coder/v2/testutil"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/coder/coder/v2/testutil"
"github.com/coder/coder/v2/cli/cliui" "github.com/coder/coder/v2/cli/cliui"
"github.com/coder/coder/v2/coderd/database/dbtime" "github.com/coder/coder/v2/coderd/database/dbtime"
"github.com/coder/coder/v2/codersdk" "github.com/coder/coder/v2/codersdk"

View File

@ -819,6 +819,7 @@ func (r *RootCmd) Server(newAPI func(context.Context, *coderd.Options) (*coderd.
Prometheus: vals.Prometheus.Enable.Value(), Prometheus: vals.Prometheus.Enable.Value(),
STUN: len(vals.DERP.Server.STUNAddresses) != 0, STUN: len(vals.DERP.Server.STUNAddresses) != 0,
Tunnel: tunnel != nil, Tunnel: tunnel != nil,
Experiments: vals.Experiments.Value(),
ParseLicenseJWT: func(lic *telemetry.License) error { ParseLicenseJWT: func(lic *telemetry.License) error {
// This will be nil when running in AGPL-only mode. // This will be nil when running in AGPL-only mode.
if options.ParseLicenseClaims == nil { if options.ParseLicenseClaims == nil {

View File

@ -54,6 +54,7 @@ type Options struct {
SnapshotFrequency time.Duration SnapshotFrequency time.Duration
Tunnel bool Tunnel bool
ParseLicenseJWT func(lic *License) error ParseLicenseJWT func(lic *License) error
Experiments []string
} }
// New constructs a reporter for telemetry data. // New constructs a reporter for telemetry data.
@ -480,6 +481,10 @@ func (r *remoteReporter) createSnapshot() (*Snapshot, error) {
} }
return nil return nil
}) })
eg.Go(func() error {
snapshot.Experiments = ConvertExperiments(r.options.Experiments)
return nil
})
err := eg.Wait() err := eg.Wait()
if err != nil { if err != nil {
@ -741,6 +746,16 @@ func ConvertExternalProvisioner(id uuid.UUID, tags map[string]string, provisione
} }
} }
func ConvertExperiments(experiments []string) []Experiment {
var out []Experiment
for _, exp := range experiments {
out = append(out, Experiment{Name: exp})
}
return out
}
// Snapshot represents a point-in-time anonymized database dump. // Snapshot represents a point-in-time anonymized database dump.
// Data is aggregated by latest on the server-side, so partial data // Data is aggregated by latest on the server-side, so partial data
// can be sent without issue. // can be sent without issue.
@ -763,6 +778,7 @@ type Snapshot struct {
WorkspaceResourceMetadata []WorkspaceResourceMetadata `json:"workspace_resource_metadata"` WorkspaceResourceMetadata []WorkspaceResourceMetadata `json:"workspace_resource_metadata"`
WorkspaceResources []WorkspaceResource `json:"workspace_resources"` WorkspaceResources []WorkspaceResource `json:"workspace_resources"`
Workspaces []Workspace `json:"workspaces"` Workspaces []Workspace `json:"workspaces"`
Experiments []Experiment `json:"experiments"`
} }
// Deployment contains information about the host running Coder. // Deployment contains information about the host running Coder.
@ -971,6 +987,10 @@ type ExternalProvisioner struct {
ShutdownAt *time.Time `json:"shutdown_at"` ShutdownAt *time.Time `json:"shutdown_at"`
} }
type Experiment struct {
Name string `json:"name"`
}
type noopReporter struct{} type noopReporter struct{}
func (*noopReporter) Report(_ *Snapshot) {} func (*noopReporter) Report(_ *Snapshot) {}

View File

@ -85,7 +85,7 @@ func TestTelemetry(t *testing.T) {
assert.NoError(t, err) assert.NoError(t, err)
_, _ = dbgen.WorkspaceProxy(t, db, database.WorkspaceProxy{}) _, _ = dbgen.WorkspaceProxy(t, db, database.WorkspaceProxy{})
_, snapshot := collectSnapshot(t, db) _, snapshot := collectSnapshot(t, db, nil)
require.Len(t, snapshot.ProvisionerJobs, 1) require.Len(t, snapshot.ProvisionerJobs, 1)
require.Len(t, snapshot.Licenses, 1) require.Len(t, snapshot.Licenses, 1)
require.Len(t, snapshot.Templates, 1) require.Len(t, snapshot.Templates, 1)
@ -110,21 +110,32 @@ func TestTelemetry(t *testing.T) {
_ = dbgen.User(t, db, database.User{ _ = dbgen.User(t, db, database.User{
Email: "kyle@coder.com", Email: "kyle@coder.com",
}) })
_, snapshot := collectSnapshot(t, db) _, snapshot := collectSnapshot(t, db, nil)
require.Len(t, snapshot.Users, 1) require.Len(t, snapshot.Users, 1)
require.Equal(t, snapshot.Users[0].EmailHashed, "bb44bf07cf9a2db0554bba63a03d822c927deae77df101874496df5a6a3e896d@coder.com") require.Equal(t, snapshot.Users[0].EmailHashed, "bb44bf07cf9a2db0554bba63a03d822c927deae77df101874496df5a6a3e896d@coder.com")
}) })
t.Run("Experiments", func(t *testing.T) {
t.Parallel()
const expName = "my-experiment"
exps := []string{expName}
_, snapshot := collectSnapshot(t, dbmem.New(), func(opts telemetry.Options) telemetry.Options {
opts.Experiments = exps
return opts
})
require.Equal(t, []telemetry.Experiment{{Name: expName}}, snapshot.Experiments)
})
} }
// nolint:paralleltest // nolint:paralleltest
func TestTelemetryInstallSource(t *testing.T) { func TestTelemetryInstallSource(t *testing.T) {
t.Setenv("CODER_TELEMETRY_INSTALL_SOURCE", "aws_marketplace") t.Setenv("CODER_TELEMETRY_INSTALL_SOURCE", "aws_marketplace")
db := dbmem.New() db := dbmem.New()
deployment, _ := collectSnapshot(t, db) deployment, _ := collectSnapshot(t, db, nil)
require.Equal(t, "aws_marketplace", deployment.InstallSource) require.Equal(t, "aws_marketplace", deployment.InstallSource)
} }
func collectSnapshot(t *testing.T, db database.Store) (*telemetry.Deployment, *telemetry.Snapshot) { func collectSnapshot(t *testing.T, db database.Store, addOptionsFn func(opts telemetry.Options) telemetry.Options) (*telemetry.Deployment, *telemetry.Snapshot) {
t.Helper() t.Helper()
deployment := make(chan *telemetry.Deployment, 64) deployment := make(chan *telemetry.Deployment, 64)
snapshot := make(chan *telemetry.Snapshot, 64) snapshot := make(chan *telemetry.Snapshot, 64)
@ -149,12 +160,17 @@ func collectSnapshot(t *testing.T, db database.Store) (*telemetry.Deployment, *t
t.Cleanup(server.Close) t.Cleanup(server.Close)
serverURL, err := url.Parse(server.URL) serverURL, err := url.Parse(server.URL)
require.NoError(t, err) require.NoError(t, err)
reporter, err := telemetry.New(telemetry.Options{ options := telemetry.Options{
Database: db, Database: db,
Logger: slogtest.Make(t, nil).Leveled(slog.LevelDebug), Logger: slogtest.Make(t, nil).Leveled(slog.LevelDebug),
URL: serverURL, URL: serverURL,
DeploymentID: uuid.NewString(), DeploymentID: uuid.NewString(),
}) }
if addOptionsFn != nil {
options = addOptionsFn(options)
}
reporter, err := telemetry.New(options)
require.NoError(t, err) require.NoError(t, err)
t.Cleanup(reporter.Close) t.Cleanup(reporter.Close)
return <-deployment, <-snapshot return <-deployment, <-snapshot