mirror of
https://github.com/coder/coder.git
synced 2025-07-09 11:45:56 +00:00
feat: add telemetry for external provisioners (#10322)
This commit is contained in:
@ -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) {}
|
||||||
|
@ -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))
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
},
|
},
|
||||||
|
@ -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,
|
||||||
|
Reference in New Issue
Block a user