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
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. // 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.
@ -705,20 +722,21 @@ type Snapshot struct {
DeploymentID string `json:"deployment_id"` DeploymentID string `json:"deployment_id"`
APIKeys []APIKey `json:"api_keys"` 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"` 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. // Deployment contains information about the host running Coder.
@ -900,6 +918,14 @@ type WorkspaceProxy struct {
UpdatedAt time.Time `json:"updated_at"` 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{} type noopReporter struct{}
func (*noopReporter) Report(_ *Snapshot) {} 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 // ServeProvisionerDaemonRequest are the parameters to call ServeProvisionerDaemon with
// @typescript-ignore ServeProvisionerDaemonRequest // @typescript-ignore ServeProvisionerDaemonRequest
type ServeProvisionerDaemonRequest struct { 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 // Organization is the organization for the URL. At present provisioner daemons ARE NOT scoped to organizations
// and so the organization ID is optional. // and so the organization ID is optional.
Organization uuid.UUID `json:"organization" format:"uuid"` 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) return nil, xerrors.Errorf("parse url: %w", err)
} }
query := serverURL.Query() query := serverURL.Query()
query.Add("id", req.ID.String())
for _, provisioner := range req.Provisioners { for _, provisioner := range req.Provisioners {
query.Add("provisioner", string(provisioner)) query.Add("provisioner", string(provisioner))
} }

View File

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

View File

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