mirror of https://github.com/coder/coder.git
feat: add telemetry for external provisioners (#10322)
This commit is contained in:
parent
9b73020f11
commit
504cedf15a
|
@ -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) {}
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Reference in New Issue