mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
chore: track terraform modules in telemetry (#15450)
Addresses https://github.com/coder/nexus/issues/35. This PR: - Adds a `workspace_modules` table to track modules used by the Terraform provisioner in provisioner jobs. - Adds a `module_path` column to the `workspace_resources` table, allowing to identify which module a resource originates from. - Starts pushing this new information into telemetry. For the person reviewing this PR, do not fret about the 1,500 new lines - ~1,000 of them are auto-generated.
This commit is contained in:
@@ -456,6 +456,17 @@ func (r *remoteReporter) createSnapshot() (*Snapshot, error) {
|
||||
}
|
||||
return nil
|
||||
})
|
||||
eg.Go(func() error {
|
||||
workspaceModules, err := r.options.Database.GetWorkspaceModulesCreatedAfter(ctx, createdAfter)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("get workspace modules: %w", err)
|
||||
}
|
||||
snapshot.WorkspaceModules = make([]WorkspaceModule, 0, len(workspaceModules))
|
||||
for _, module := range workspaceModules {
|
||||
snapshot.WorkspaceModules = append(snapshot.WorkspaceModules, ConvertWorkspaceModule(module))
|
||||
}
|
||||
return nil
|
||||
})
|
||||
eg.Go(func() error {
|
||||
licenses, err := r.options.Database.GetUnexpiredLicenses(ctx)
|
||||
if err != nil {
|
||||
@@ -642,7 +653,7 @@ func ConvertWorkspaceApp(app database.WorkspaceApp) WorkspaceApp {
|
||||
|
||||
// ConvertWorkspaceResource anonymizes a workspace resource.
|
||||
func ConvertWorkspaceResource(resource database.WorkspaceResource) WorkspaceResource {
|
||||
return WorkspaceResource{
|
||||
r := WorkspaceResource{
|
||||
ID: resource.ID,
|
||||
JobID: resource.JobID,
|
||||
CreatedAt: resource.CreatedAt,
|
||||
@@ -650,6 +661,10 @@ func ConvertWorkspaceResource(resource database.WorkspaceResource) WorkspaceReso
|
||||
Type: resource.Type,
|
||||
InstanceType: resource.InstanceType.String,
|
||||
}
|
||||
if resource.ModulePath.Valid {
|
||||
r.ModulePath = &resource.ModulePath.String
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// ConvertWorkspaceResourceMetadata anonymizes workspace metadata.
|
||||
@@ -661,6 +676,29 @@ func ConvertWorkspaceResourceMetadata(metadata database.WorkspaceResourceMetadat
|
||||
}
|
||||
}
|
||||
|
||||
func shouldSendRawModuleSource(source string) bool {
|
||||
return strings.Contains(source, "registry.coder.com")
|
||||
}
|
||||
|
||||
func ConvertWorkspaceModule(module database.WorkspaceModule) WorkspaceModule {
|
||||
source := module.Source
|
||||
version := module.Version
|
||||
if !shouldSendRawModuleSource(source) {
|
||||
source = fmt.Sprintf("%x", sha256.Sum256([]byte(source)))
|
||||
version = fmt.Sprintf("%x", sha256.Sum256([]byte(version)))
|
||||
}
|
||||
|
||||
return WorkspaceModule{
|
||||
ID: module.ID,
|
||||
JobID: module.JobID,
|
||||
Transition: module.Transition,
|
||||
Source: source,
|
||||
Version: version,
|
||||
Key: module.Key,
|
||||
CreatedAt: module.CreatedAt,
|
||||
}
|
||||
}
|
||||
|
||||
// ConvertUser anonymizes a user.
|
||||
func ConvertUser(dbUser database.User) User {
|
||||
emailHashed := ""
|
||||
@@ -810,6 +848,7 @@ type Snapshot struct {
|
||||
WorkspaceProxies []WorkspaceProxy `json:"workspace_proxies"`
|
||||
WorkspaceResourceMetadata []WorkspaceResourceMetadata `json:"workspace_resource_metadata"`
|
||||
WorkspaceResources []WorkspaceResource `json:"workspace_resources"`
|
||||
WorkspaceModules []WorkspaceModule `json:"workspace_modules"`
|
||||
Workspaces []Workspace `json:"workspaces"`
|
||||
NetworkEvents []NetworkEvent `json:"network_events"`
|
||||
}
|
||||
@@ -878,6 +917,11 @@ type WorkspaceResource struct {
|
||||
Transition database.WorkspaceTransition `json:"transition"`
|
||||
Type string `json:"type"`
|
||||
InstanceType string `json:"instance_type"`
|
||||
// ModulePath is nullable because it was added a long time after the
|
||||
// original workspace resource telemetry was added. All new resources
|
||||
// will have a module path, but deployments with older resources still
|
||||
// in the database will not.
|
||||
ModulePath *string `json:"module_path"`
|
||||
}
|
||||
|
||||
type WorkspaceResourceMetadata struct {
|
||||
@@ -886,6 +930,16 @@ type WorkspaceResourceMetadata struct {
|
||||
Sensitive bool `json:"sensitive"`
|
||||
}
|
||||
|
||||
type WorkspaceModule struct {
|
||||
ID uuid.UUID `json:"id"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
JobID uuid.UUID `json:"job_id"`
|
||||
Transition database.WorkspaceTransition `json:"transition"`
|
||||
Key string `json:"key"`
|
||||
Version string `json:"version"`
|
||||
Source string `json:"source"`
|
||||
}
|
||||
|
||||
type WorkspaceAgent struct {
|
||||
ID uuid.UUID `json:"id"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"sort"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -20,6 +21,7 @@ import (
|
||||
"github.com/coder/coder/v2/coderd/database"
|
||||
"github.com/coder/coder/v2/coderd/database/dbgen"
|
||||
"github.com/coder/coder/v2/coderd/database/dbmem"
|
||||
"github.com/coder/coder/v2/coderd/database/dbtestutil"
|
||||
"github.com/coder/coder/v2/coderd/database/dbtime"
|
||||
"github.com/coder/coder/v2/coderd/telemetry"
|
||||
"github.com/coder/coder/v2/testutil"
|
||||
@@ -87,6 +89,8 @@ func TestTelemetry(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
_, _ = dbgen.WorkspaceProxy(t, db, database.WorkspaceProxy{})
|
||||
|
||||
_ = dbgen.WorkspaceModule(t, db, database.WorkspaceModule{})
|
||||
|
||||
_, snapshot := collectSnapshot(t, db, nil)
|
||||
require.Len(t, snapshot.ProvisionerJobs, 1)
|
||||
require.Len(t, snapshot.Licenses, 1)
|
||||
@@ -103,6 +107,7 @@ func TestTelemetry(t *testing.T) {
|
||||
require.Len(t, snapshot.WorkspaceResources, 1)
|
||||
require.Len(t, snapshot.WorkspaceAgentStats, 1)
|
||||
require.Len(t, snapshot.WorkspaceProxies, 1)
|
||||
require.Len(t, snapshot.WorkspaceModules, 1)
|
||||
|
||||
wsa := snapshot.WorkspaceAgents[0]
|
||||
require.Len(t, wsa.Subsystems, 2)
|
||||
@@ -119,6 +124,31 @@ func TestTelemetry(t *testing.T) {
|
||||
require.Len(t, snapshot.Users, 1)
|
||||
require.Equal(t, snapshot.Users[0].EmailHashed, "bb44bf07cf9a2db0554bba63a03d822c927deae77df101874496df5a6a3e896d@coder.com")
|
||||
})
|
||||
t.Run("HashedModule", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
db, _ := dbtestutil.NewDB(t)
|
||||
pj := dbgen.ProvisionerJob(t, db, nil, database.ProvisionerJob{})
|
||||
_ = dbgen.WorkspaceModule(t, db, database.WorkspaceModule{
|
||||
JobID: pj.ID,
|
||||
Source: "registry.coder.com/terraform/aws",
|
||||
Version: "1.0.0",
|
||||
})
|
||||
_ = dbgen.WorkspaceModule(t, db, database.WorkspaceModule{
|
||||
JobID: pj.ID,
|
||||
Source: "internal-url.com/some-module",
|
||||
Version: "1.0.0",
|
||||
})
|
||||
_, snapshot := collectSnapshot(t, db, nil)
|
||||
require.Len(t, snapshot.WorkspaceModules, 2)
|
||||
modules := snapshot.WorkspaceModules
|
||||
sort.Slice(modules, func(i, j int) bool {
|
||||
return modules[i].Source < modules[j].Source
|
||||
})
|
||||
require.Equal(t, modules[0].Source, "921c61d6f3eef5118f3cae658d1518b378c5b02a4955a766c791440894d989c5")
|
||||
require.Equal(t, modules[0].Version, "92521fc3cbd964bdc9f584a991b89fddaa5754ed1cc96d6d42445338669c1305")
|
||||
require.Equal(t, modules[1].Source, "registry.coder.com/terraform/aws")
|
||||
require.Equal(t, modules[1].Version, "1.0.0")
|
||||
})
|
||||
}
|
||||
|
||||
// nolint:paralleltest
|
||||
|
||||
Reference in New Issue
Block a user