diff --git a/.vscode/settings.json b/.vscode/settings.json index 12ef68ca9e..f4d9255c0b 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -37,6 +37,7 @@ "Jobf", "Keygen", "kirsle", + "Kubernetes", "ldflags", "manifoldco", "mapstructure", diff --git a/coderd/database/databasefake/databasefake.go b/coderd/database/databasefake/databasefake.go index 9471c843a8..bf88994256 100644 --- a/coderd/database/databasefake/databasefake.go +++ b/coderd/database/databasefake/databasefake.go @@ -1361,6 +1361,26 @@ func (q *fakeQuerier) GetWorkspaceResourcesCreatedAfter(_ context.Context, after return resources, nil } +func (q *fakeQuerier) GetWorkspaceResourceMetadataCreatedAfter(ctx context.Context, after time.Time) ([]database.WorkspaceResourceMetadatum, error) { + resources, err := q.GetWorkspaceResourcesCreatedAfter(ctx, after) + if err != nil { + return nil, err + } + resourceIDs := map[uuid.UUID]struct{}{} + for _, resource := range resources { + resourceIDs[resource.ID] = struct{}{} + } + metadata := make([]database.WorkspaceResourceMetadatum, 0) + for _, m := range q.provisionerJobResourceMetadata { + _, ok := resourceIDs[m.WorkspaceResourceID] + if !ok { + continue + } + metadata = append(metadata, m) + } + return metadata, nil +} + func (q *fakeQuerier) GetWorkspaceResourceMetadataByResourceID(_ context.Context, id uuid.UUID) ([]database.WorkspaceResourceMetadatum, error) { q.mutex.RLock() defer q.mutex.RUnlock() diff --git a/coderd/database/querier.go b/coderd/database/querier.go index 272efe40fd..2cc1a2b084 100644 --- a/coderd/database/querier.go +++ b/coderd/database/querier.go @@ -89,6 +89,7 @@ type querier interface { GetWorkspaceResourceByID(ctx context.Context, id uuid.UUID) (WorkspaceResource, error) GetWorkspaceResourceMetadataByResourceID(ctx context.Context, workspaceResourceID uuid.UUID) ([]WorkspaceResourceMetadatum, error) GetWorkspaceResourceMetadataByResourceIDs(ctx context.Context, ids []uuid.UUID) ([]WorkspaceResourceMetadatum, error) + GetWorkspaceResourceMetadataCreatedAfter(ctx context.Context, createdAt time.Time) ([]WorkspaceResourceMetadatum, error) GetWorkspaceResourcesByJobID(ctx context.Context, jobID uuid.UUID) ([]WorkspaceResource, error) GetWorkspaceResourcesCreatedAfter(ctx context.Context, createdAt time.Time) ([]WorkspaceResource, error) GetWorkspaces(ctx context.Context, arg GetWorkspacesParams) ([]Workspace, error) diff --git a/coderd/database/queries.sql.go b/coderd/database/queries.sql.go index 5fd32ad674..b4fc10691f 100644 --- a/coderd/database/queries.sql.go +++ b/coderd/database/queries.sql.go @@ -4165,6 +4165,40 @@ func (q *sqlQuerier) GetWorkspaceResourceMetadataByResourceIDs(ctx context.Conte return items, nil } +const getWorkspaceResourceMetadataCreatedAfter = `-- name: GetWorkspaceResourceMetadataCreatedAfter :many +SELECT workspace_resource_id, key, value, sensitive FROM workspace_resource_metadata WHERE workspace_resource_id = ANY( + SELECT id FROM workspace_resources WHERE created_at > $1 +) +` + +func (q *sqlQuerier) GetWorkspaceResourceMetadataCreatedAfter(ctx context.Context, createdAt time.Time) ([]WorkspaceResourceMetadatum, error) { + rows, err := q.db.QueryContext(ctx, getWorkspaceResourceMetadataCreatedAfter, createdAt) + if err != nil { + return nil, err + } + defer rows.Close() + var items []WorkspaceResourceMetadatum + for rows.Next() { + var i WorkspaceResourceMetadatum + if err := rows.Scan( + &i.WorkspaceResourceID, + &i.Key, + &i.Value, + &i.Sensitive, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + const getWorkspaceResourcesByJobID = `-- name: GetWorkspaceResourcesByJobID :many SELECT id, created_at, job_id, transition, type, name diff --git a/coderd/database/queries/workspaceresources.sql b/coderd/database/queries/workspaceresources.sql index 6849d6ff58..d317e9db27 100644 --- a/coderd/database/queries/workspaceresources.sql +++ b/coderd/database/queries/workspaceresources.sql @@ -44,3 +44,8 @@ INSERT INTO workspace_resource_metadata (workspace_resource_id, key, value, sensitive) VALUES ($1, $2, $3, $4) RETURNING *; + +-- name: GetWorkspaceResourceMetadataCreatedAfter :many +SELECT * FROM workspace_resource_metadata WHERE workspace_resource_id = ANY( + SELECT id FROM workspace_resources WHERE created_at > $1 +); diff --git a/coderd/telemetry/telemetry.go b/coderd/telemetry/telemetry.go index c52bbec33f..d4b6d42db8 100644 --- a/coderd/telemetry/telemetry.go +++ b/coderd/telemetry/telemetry.go @@ -10,6 +10,7 @@ import ( "net" "net/http" "net/url" + "os" "runtime" "strings" "sync" @@ -230,6 +231,7 @@ func (r *remoteReporter) deployment() error { Architecture: sysInfo.Architecture, BuiltinPostgres: r.options.BuiltinPostgres, Containerized: containerized, + Kubernetes: os.Getenv("KUBERNETES_SERVICE_HOST") != "", GitHubOAuth: r.options.GitHubOAuth, OIDCAuth: r.options.OIDCAuth, OIDCIssuerURL: r.options.OIDCIssuerURL, @@ -423,6 +425,17 @@ func (r *remoteReporter) createSnapshot() (*Snapshot, error) { } return nil }) + eg.Go(func() error { + workspaceMetadata, err := r.options.Database.GetWorkspaceResourceMetadataCreatedAfter(ctx, createdAfter) + if err != nil { + return xerrors.Errorf("get workspace resource metadata: %w", err) + } + snapshot.WorkspaceResourceMetadata = make([]WorkspaceResourceMetadata, 0, len(workspaceMetadata)) + for _, metadata := range workspaceMetadata { + snapshot.WorkspaceResourceMetadata = append(snapshot.WorkspaceResourceMetadata, ConvertWorkspaceResourceMetadata(metadata)) + } + return nil + }) err := eg.Wait() if err != nil { @@ -531,6 +544,15 @@ func ConvertWorkspaceResource(resource database.WorkspaceResource) WorkspaceReso } } +// ConvertWorkspaceResourceMetadata anonymizes workspace metadata. +func ConvertWorkspaceResourceMetadata(metadata database.WorkspaceResourceMetadatum) WorkspaceResourceMetadata { + return WorkspaceResourceMetadata{ + ResourceID: metadata.WorkspaceResourceID, + Key: metadata.Key, + Sensitive: metadata.Sensitive, + } +} + // ConvertUser anonymizes a user. func ConvertUser(dbUser database.User) User { emailHashed := "" @@ -584,17 +606,18 @@ func ConvertTemplateVersion(version database.TemplateVersion) TemplateVersion { type Snapshot struct { DeploymentID string `json:"deployment_id"` - APIKeys []APIKey `json:"api_keys"` - ParameterSchemas []ParameterSchema `json:"parameter_schemas"` - ProvisionerJobs []ProvisionerJob `json:"provisioner_jobs"` - Templates []Template `json:"templates"` - TemplateVersions []TemplateVersion `json:"template_versions"` - Users []User `json:"users"` - Workspaces []Workspace `json:"workspaces"` - WorkspaceApps []WorkspaceApp `json:"workspace_apps"` - WorkspaceAgents []WorkspaceAgent `json:"workspace_agents"` - WorkspaceBuilds []WorkspaceBuild `json:"workspace_build"` - WorkspaceResources []WorkspaceResource `json:"workspace_resources"` + APIKeys []APIKey `json:"api_keys"` + ParameterSchemas []ParameterSchema `json:"parameter_schemas"` + ProvisionerJobs []ProvisionerJob `json:"provisioner_jobs"` + Templates []Template `json:"templates"` + TemplateVersions []TemplateVersion `json:"template_versions"` + Users []User `json:"users"` + Workspaces []Workspace `json:"workspaces"` + WorkspaceApps []WorkspaceApp `json:"workspace_apps"` + WorkspaceAgents []WorkspaceAgent `json:"workspace_agents"` + WorkspaceBuilds []WorkspaceBuild `json:"workspace_build"` + WorkspaceResources []WorkspaceResource `json:"workspace_resources"` + WorkspaceResourceMetadata []WorkspaceResourceMetadata `json:"workspace_resource_metadata"` } // Deployment contains information about the host running Coder. @@ -603,6 +626,7 @@ type Deployment struct { Architecture string `json:"architecture"` BuiltinPostgres bool `json:"builtin_postgres"` Containerized bool `json:"containerized"` + Kubernetes bool `json:"kubernetes"` Tunnel bool `json:"tunnel"` GitHubOAuth bool `json:"github_oauth"` OIDCAuth bool `json:"oidc_auth"` @@ -647,6 +671,12 @@ type WorkspaceResource struct { Type string `json:"type"` } +type WorkspaceResourceMetadata struct { + ResourceID uuid.UUID `json:"resource_id"` + Key string `json:"key"` + Sensitive bool `json:"sensitive"` +} + type WorkspaceAgent struct { ID uuid.UUID `json:"id"` CreatedAt time.Time `json:"created_at"`