Skip to content

Commit 504cedf

Browse files
authored
feat: add telemetry for external provisioners (#10322)
1 parent 9b73020 commit 504cedf

File tree

4 files changed

+56
-9
lines changed

4 files changed

+56
-9
lines changed

coderd/telemetry/telemetry.go

+34-8
Original file line numberDiff line numberDiff line change
@@ -698,27 +698,45 @@ func ConvertWorkspaceProxy(proxy database.WorkspaceProxy) WorkspaceProxy {
698698
}
699699
}
700700

701+
func ConvertExternalProvisioner(id uuid.UUID, tags map[string]string, provisioners []database.ProvisionerType) ExternalProvisioner {
702+
tagsCopy := make(map[string]string, len(tags))
703+
for k, v := range tags {
704+
tagsCopy[k] = v
705+
}
706+
strProvisioners := make([]string, 0, len(provisioners))
707+
for _, prov := range provisioners {
708+
strProvisioners = append(strProvisioners, string(prov))
709+
}
710+
return ExternalProvisioner{
711+
ID: id.String(),
712+
Tags: tagsCopy,
713+
Provisioners: strProvisioners,
714+
StartedAt: time.Now(),
715+
}
716+
}
717+
701718
// Snapshot represents a point-in-time anonymized database dump.
702719
// Data is aggregated by latest on the server-side, so partial data
703720
// can be sent without issue.
704721
type Snapshot struct {
705722
DeploymentID string `json:"deployment_id"`
706723

707724
APIKeys []APIKey `json:"api_keys"`
708-
ProvisionerJobs []ProvisionerJob `json:"provisioner_jobs"`
725+
CLIInvocations []clitelemetry.Invocation `json:"cli_invocations"`
726+
ExternalProvisioners []ExternalProvisioner `json:"external_provisioners"`
709727
Licenses []License `json:"licenses"`
710-
Templates []Template `json:"templates"`
728+
ProvisionerJobs []ProvisionerJob `json:"provisioner_jobs"`
711729
TemplateVersions []TemplateVersion `json:"template_versions"`
730+
Templates []Template `json:"templates"`
712731
Users []User `json:"users"`
713-
Workspaces []Workspace `json:"workspaces"`
714-
WorkspaceApps []WorkspaceApp `json:"workspace_apps"`
715-
WorkspaceAgents []WorkspaceAgent `json:"workspace_agents"`
716732
WorkspaceAgentStats []WorkspaceAgentStat `json:"workspace_agent_stats"`
733+
WorkspaceAgents []WorkspaceAgent `json:"workspace_agents"`
734+
WorkspaceApps []WorkspaceApp `json:"workspace_apps"`
717735
WorkspaceBuilds []WorkspaceBuild `json:"workspace_build"`
718-
WorkspaceResources []WorkspaceResource `json:"workspace_resources"`
719-
WorkspaceResourceMetadata []WorkspaceResourceMetadata `json:"workspace_resource_metadata"`
720736
WorkspaceProxies []WorkspaceProxy `json:"workspace_proxies"`
721-
CLIInvocations []clitelemetry.Invocation `json:"cli_invocations"`
737+
WorkspaceResourceMetadata []WorkspaceResourceMetadata `json:"workspace_resource_metadata"`
738+
WorkspaceResources []WorkspaceResource `json:"workspace_resources"`
739+
Workspaces []Workspace `json:"workspaces"`
722740
}
723741

724742
// Deployment contains information about the host running Coder.
@@ -900,6 +918,14 @@ type WorkspaceProxy struct {
900918
UpdatedAt time.Time `json:"updated_at"`
901919
}
902920

921+
type ExternalProvisioner struct {
922+
ID string `json:"id"`
923+
Tags map[string]string `json:"tags"`
924+
Provisioners []string `json:"provisioners"`
925+
StartedAt time.Time `json:"started_at"`
926+
ShutdownAt *time.Time `json:"shutdown_at"`
927+
}
928+
903929
type noopReporter struct{}
904930

905931
func (*noopReporter) Report(_ *Snapshot) {}

codersdk/provisionerdaemons.go

+3
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,8 @@ func (c *Client) provisionerJobLogsAfter(ctx context.Context, path string, after
174174
// ServeProvisionerDaemonRequest are the parameters to call ServeProvisionerDaemon with
175175
// @typescript-ignore ServeProvisionerDaemonRequest
176176
type ServeProvisionerDaemonRequest struct {
177+
// ID is a unique ID for a provisioner daemon.
178+
ID uuid.UUID `json:"id" format:"uuid"`
177179
// Organization is the organization for the URL. At present provisioner daemons ARE NOT scoped to organizations
178180
// and so the organization ID is optional.
179181
Organization uuid.UUID `json:"organization" format:"uuid"`
@@ -194,6 +196,7 @@ func (c *Client) ServeProvisionerDaemon(ctx context.Context, req ServeProvisione
194196
return nil, xerrors.Errorf("parse url: %w", err)
195197
}
196198
query := serverURL.Query()
199+
query.Add("id", req.ID.String())
197200
for _, provisioner := range req.Provisioners {
198201
query.Add("provisioner", string(provisioner))
199202
}

enterprise/cli/provisionerdaemons.go

+3
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"os/signal"
1010
"time"
1111

12+
"github.com/google/uuid"
1213
"golang.org/x/xerrors"
1314

1415
"cdr.dev/slog"
@@ -127,8 +128,10 @@ func (r *RootCmd) provisionerDaemonStart() *clibase.Cmd {
127128
connector := provisionerd.LocalProvisioners{
128129
string(database.ProvisionerTypeTerraform): proto.NewDRPCProvisionerClient(terraformClient),
129130
}
131+
id := uuid.New()
130132
srv := provisionerd.New(func(ctx context.Context) (provisionerdproto.DRPCProvisionerDaemonClient, error) {
131133
return client.ServeProvisionerDaemon(ctx, codersdk.ServeProvisionerDaemonRequest{
134+
ID: id,
132135
Provisioners: []codersdk.ProvisionerType{
133136
codersdk.ProvisionerTypeTerraform,
134137
},

enterprise/coderd/provisionerdaemons.go

+16-1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"net"
1111
"net/http"
1212
"strings"
13+
"time"
1314

1415
"github.com/google/uuid"
1516
"github.com/hashicorp/yamux"
@@ -27,6 +28,8 @@ import (
2728
"github.com/coder/coder/v2/coderd/httpmw"
2829
"github.com/coder/coder/v2/coderd/provisionerdserver"
2930
"github.com/coder/coder/v2/coderd/rbac"
31+
"github.com/coder/coder/v2/coderd/telemetry"
32+
"github.com/coder/coder/v2/coderd/util/ptr"
3033
"github.com/coder/coder/v2/codersdk"
3134
"github.com/coder/coder/v2/provisionerd/proto"
3235
)
@@ -155,6 +158,11 @@ func (api *API) provisionerDaemonServe(rw http.ResponseWriter, r *http.Request)
155158
return
156159
}
157160

161+
id, _ := uuid.Parse(r.URL.Query().Get("id"))
162+
if id == uuid.Nil {
163+
id = uuid.New()
164+
}
165+
158166
provisionersMap := map[codersdk.ProvisionerType]struct{}{}
159167
for _, provisioner := range r.URL.Query()["provisioner"] {
160168
switch provisioner {
@@ -210,6 +218,13 @@ func (api *API) provisionerDaemonServe(rw http.ResponseWriter, r *http.Request)
210218
api.AGPL.WebsocketWaitMutex.Unlock()
211219
defer api.AGPL.WebsocketWaitGroup.Done()
212220

221+
tep := telemetry.ConvertExternalProvisioner(id, tags, provisioners)
222+
api.Telemetry.Report(&telemetry.Snapshot{ExternalProvisioners: []telemetry.ExternalProvisioner{tep}})
223+
defer func() {
224+
tep.ShutdownAt = ptr.Ref(time.Now())
225+
api.Telemetry.Report(&telemetry.Snapshot{ExternalProvisioners: []telemetry.ExternalProvisioner{tep}})
226+
}()
227+
213228
conn, err := websocket.Accept(rw, r, &websocket.AcceptOptions{
214229
// Need to disable compression to avoid a data-race.
215230
CompressionMode: websocket.CompressionDisabled,
@@ -245,7 +260,7 @@ func (api *API) provisionerDaemonServe(rw http.ResponseWriter, r *http.Request)
245260
srv, err := provisionerdserver.NewServer(
246261
api.ctx,
247262
api.AccessURL,
248-
uuid.New(),
263+
id,
249264
logger,
250265
provisioners,
251266
tags,

0 commit comments

Comments
 (0)