feat: add telemetry for external provisioners (#10322)

This commit is contained in:
Colin Adler 2023-10-18 14:20:30 -05:00 committed by GitHub
parent 9b73020f11
commit 504cedf15a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 61 additions and 14 deletions

View File

@ -698,6 +698,23 @@ func ConvertWorkspaceProxy(proxy database.WorkspaceProxy) WorkspaceProxy {
}
}
func ConvertExternalProvisioner(id uuid.UUID, tags map[string]string, provisioners []database.ProvisionerType) ExternalProvisioner {
tagsCopy := make(map[string]string, len(tags))
for k, v := range tags {
tagsCopy[k] = v
}
strProvisioners := make([]string, 0, len(provisioners))
for _, prov := range provisioners {
strProvisioners = append(strProvisioners, string(prov))
}
return ExternalProvisioner{
ID: id.String(),
Tags: tagsCopy,
Provisioners: strProvisioners,
StartedAt: time.Now(),
}
}
// Snapshot represents a point-in-time anonymized database dump.
// Data is aggregated by latest on the server-side, so partial data
// can be sent without issue.
@ -705,20 +722,21 @@ type Snapshot struct {
DeploymentID string `json:"deployment_id"`
APIKeys []APIKey `json:"api_keys"`
ProvisionerJobs []ProvisionerJob `json:"provisioner_jobs"`
Licenses []License `json:"licenses"`
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"`
WorkspaceAgentStats []WorkspaceAgentStat `json:"workspace_agent_stats"`
WorkspaceBuilds []WorkspaceBuild `json:"workspace_build"`
WorkspaceResources []WorkspaceResource `json:"workspace_resources"`
WorkspaceResourceMetadata []WorkspaceResourceMetadata `json:"workspace_resource_metadata"`
WorkspaceProxies []WorkspaceProxy `json:"workspace_proxies"`
CLIInvocations []clitelemetry.Invocation `json:"cli_invocations"`
ExternalProvisioners []ExternalProvisioner `json:"external_provisioners"`
Licenses []License `json:"licenses"`
ProvisionerJobs []ProvisionerJob `json:"provisioner_jobs"`
TemplateVersions []TemplateVersion `json:"template_versions"`
Templates []Template `json:"templates"`
Users []User `json:"users"`
WorkspaceAgentStats []WorkspaceAgentStat `json:"workspace_agent_stats"`
WorkspaceAgents []WorkspaceAgent `json:"workspace_agents"`
WorkspaceApps []WorkspaceApp `json:"workspace_apps"`
WorkspaceBuilds []WorkspaceBuild `json:"workspace_build"`
WorkspaceProxies []WorkspaceProxy `json:"workspace_proxies"`
WorkspaceResourceMetadata []WorkspaceResourceMetadata `json:"workspace_resource_metadata"`
WorkspaceResources []WorkspaceResource `json:"workspace_resources"`
Workspaces []Workspace `json:"workspaces"`
}
// Deployment contains information about the host running Coder.
@ -900,6 +918,14 @@ type WorkspaceProxy struct {
UpdatedAt time.Time `json:"updated_at"`
}
type ExternalProvisioner struct {
ID string `json:"id"`
Tags map[string]string `json:"tags"`
Provisioners []string `json:"provisioners"`
StartedAt time.Time `json:"started_at"`
ShutdownAt *time.Time `json:"shutdown_at"`
}
type noopReporter struct{}
func (*noopReporter) Report(_ *Snapshot) {}

View File

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

View File

@ -9,6 +9,7 @@ import (
"os/signal"
"time"
"github.com/google/uuid"
"golang.org/x/xerrors"
"cdr.dev/slog"
@ -127,8 +128,10 @@ func (r *RootCmd) provisionerDaemonStart() *clibase.Cmd {
connector := provisionerd.LocalProvisioners{
string(database.ProvisionerTypeTerraform): proto.NewDRPCProvisionerClient(terraformClient),
}
id := uuid.New()
srv := provisionerd.New(func(ctx context.Context) (provisionerdproto.DRPCProvisionerDaemonClient, error) {
return client.ServeProvisionerDaemon(ctx, codersdk.ServeProvisionerDaemonRequest{
ID: id,
Provisioners: []codersdk.ProvisionerType{
codersdk.ProvisionerTypeTerraform,
},

View File

@ -10,6 +10,7 @@ import (
"net"
"net/http"
"strings"
"time"
"github.com/google/uuid"
"github.com/hashicorp/yamux"
@ -27,6 +28,8 @@ import (
"github.com/coder/coder/v2/coderd/httpmw"
"github.com/coder/coder/v2/coderd/provisionerdserver"
"github.com/coder/coder/v2/coderd/rbac"
"github.com/coder/coder/v2/coderd/telemetry"
"github.com/coder/coder/v2/coderd/util/ptr"
"github.com/coder/coder/v2/codersdk"
"github.com/coder/coder/v2/provisionerd/proto"
)
@ -155,6 +158,11 @@ func (api *API) provisionerDaemonServe(rw http.ResponseWriter, r *http.Request)
return
}
id, _ := uuid.Parse(r.URL.Query().Get("id"))
if id == uuid.Nil {
id = uuid.New()
}
provisionersMap := map[codersdk.ProvisionerType]struct{}{}
for _, provisioner := range r.URL.Query()["provisioner"] {
switch provisioner {
@ -210,6 +218,13 @@ func (api *API) provisionerDaemonServe(rw http.ResponseWriter, r *http.Request)
api.AGPL.WebsocketWaitMutex.Unlock()
defer api.AGPL.WebsocketWaitGroup.Done()
tep := telemetry.ConvertExternalProvisioner(id, tags, provisioners)
api.Telemetry.Report(&telemetry.Snapshot{ExternalProvisioners: []telemetry.ExternalProvisioner{tep}})
defer func() {
tep.ShutdownAt = ptr.Ref(time.Now())
api.Telemetry.Report(&telemetry.Snapshot{ExternalProvisioners: []telemetry.ExternalProvisioner{tep}})
}()
conn, err := websocket.Accept(rw, r, &websocket.AcceptOptions{
// Need to disable compression to avoid a data-race.
CompressionMode: websocket.CompressionDisabled,
@ -245,7 +260,7 @@ func (api *API) provisionerDaemonServe(rw http.ResponseWriter, r *http.Request)
srv, err := provisionerdserver.NewServer(
api.ctx,
api.AccessURL,
uuid.New(),
id,
logger,
provisioners,
tags,