Skip to content

feat: add cross-origin reporting for telemetry in the dashboard #13612

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Jun 20, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Respect the telemetry flag
  • Loading branch information
kylecarbs committed Jun 20, 2024
commit 9b5c997f48fa1d77d13d2a13e244ecc11be9628d
4 changes: 4 additions & 0 deletions coderd/apidoc/docs.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions coderd/apidoc/swagger.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions coderd/coderd.go
Original file line number Diff line number Diff line change
Expand Up @@ -447,6 +447,7 @@ func New(options *Options) *API {
WorkspaceProxy: false,
UpgradeMessage: api.DeploymentValues.CLIUpgradeMessage.String(),
DeploymentID: api.DeploymentID,
Telemetry: api.Telemetry.Enabled(),
}
api.SiteHandler = site.New(&site.Options{
BinFS: binFS,
Expand Down
6 changes: 6 additions & 0 deletions coderd/telemetry/telemetry.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ type Reporter interface {
// database. For example, if a new user is added, a snapshot can
// contain just that user entry.
Report(snapshot *Snapshot)
Enabled() bool
Close()
}

Expand All @@ -109,6 +110,10 @@ type remoteReporter struct {
shutdownAt *time.Time
}

func (r *remoteReporter) Enabled() bool {
return true
}

func (r *remoteReporter) Report(snapshot *Snapshot) {
go r.reportSync(snapshot)
}
Expand Down Expand Up @@ -948,4 +953,5 @@ type ExternalProvisioner struct {
type noopReporter struct{}

func (*noopReporter) Report(_ *Snapshot) {}
func (*noopReporter) Enabled() bool { return false }
func (*noopReporter) Close() {}
3 changes: 2 additions & 1 deletion codersdk/deployment.go
Original file line number Diff line number Diff line change
Expand Up @@ -2173,11 +2173,12 @@ type BuildInfoResponse struct {
ExternalURL string `json:"external_url"`
// Version returns the semantic version of the build.
Version string `json:"version"`

// DashboardURL is the URL to hit the deployment's dashboard.
// For external workspace proxies, this is the coderd they are connected
// to.
DashboardURL string `json:"dashboard_url"`
// Telemetry is a boolean that indicates whether telemetry is enabled.
Telemetry bool `json:"telemetry"`

WorkspaceProxy bool `json:"workspace_proxy"`

Expand Down
1 change: 1 addition & 0 deletions docs/api/general.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions docs/api/schemas.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion enterprise/wsproxy/wsproxysdk/wsproxysdk_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ func TestDialCoordinator(t *testing.T) {
Node: &proto.Node{
Id: 55,
AsOf: timestamppb.New(time.Unix(1689653252, 0)),
Key: peerNodeKey[:],
Key: peerNodeKey,
Disco: string(peerDiscoKey),
PreferredDerp: 0,
DerpLatency: map[string]float64{
Expand Down
1 change: 1 addition & 0 deletions site/src/api/typesGenerated.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions site/src/hooks/useEmbeddedMetadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export const DEFAULT_METADATA_KEY = "property";
* attributes
*/
type AvailableMetadata = Readonly<{
telemetry: boolean;
user: User;
experiments: Experiments;
appearance: AppearanceConfig;
Expand Down Expand Up @@ -81,6 +82,7 @@ export class MetadataManager implements MetadataManagerApi {
this.trackedMetadataNodes = new Map();

this.metadata = {
telemetry: this.registerValue("telemetry"),
user: this.registerValue<User>("user"),
appearance: this.registerValue<AppearanceConfig>("appearance"),
entitlements: this.registerValue<Entitlements>("entitlements"),
Expand Down
33 changes: 17 additions & 16 deletions site/src/pages/LoginPage/LoginPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,14 @@ export const LoginPage: FC = () => {
const buildInfoQuery = useQuery(buildInfo(metadata["build-info"]));

if (isSignedIn) {
// This uses `navigator.sendBeacon`, so window.href
// will not stop the request from being sent!
sendDeploymentEvent({
type: "deployment_login",
// This should work most of the time because of embedded
// metadata and the user being logged in.
deployment_id: buildInfoQuery.data?.deployment_id || "",
user_id: user?.id,
})
if (buildInfoQuery.data) {
// This uses `navigator.sendBeacon`, so window.href
// will not stop the request from being sent!
sendDeploymentEvent(buildInfoQuery.data, {
type: "deployment_login",
user_id: user?.id,
});
}

// If the redirect is going to a workspace application, and we
// are missing authentication, then we need to change the href location
Expand Down Expand Up @@ -86,13 +85,15 @@ export const LoginPage: FC = () => {
isSigningIn={isSigningIn}
onSignIn={async ({ email, password }) => {
await signIn(email, password);
// This uses `navigator.sendBeacon`, so navigating away
// will not prevent it!
sendDeploymentEvent({
type: "deployment_login",
deployment_id: buildInfoQuery.data?.deployment_id || "",
user_id: user?.id,
})
if (buildInfoQuery.data) {
// This uses `navigator.sendBeacon`, so navigating away
// will not prevent it!
sendDeploymentEvent(buildInfoQuery.data, {
type: "deployment_login",
user_id: user?.id,
});
}

navigate("/");
}}
/>
Expand Down
24 changes: 16 additions & 8 deletions site/src/pages/SetupPage/SetupPage.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -121,12 +121,20 @@ describe("Setup Page", () => {
);
await waitForLoaderToBeRemoved();
await waitFor(() => {
expect(navigator.sendBeacon).toBeCalledWith("https://coder.com/api/track-deployment", new Blob([JSON.stringify({
type: "deployment_setup",
deployment_id: MockBuildInfo.deployment_id,
})], {
type: "application/json",
}))
})
})
expect(navigator.sendBeacon).toBeCalledWith(
"https://coder.com/api/track-deployment",
new Blob(
[
JSON.stringify({
type: "deployment_setup",
deployment_id: MockBuildInfo.deployment_id,
}),
],
{
type: "application/json",
},
),
);
});
});
});
16 changes: 5 additions & 11 deletions site/src/pages/SetupPage/SetupPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,7 @@ import { pageTitle } from "utils/page";
import { sendDeploymentEvent } from "utils/telemetry";
import { SetupPageView } from "./SetupPageView";

export const SetupPage: FC<{
telemetryURL?: string;
}> = ({ telemetryURL }) => {
export const SetupPage: FC = () => {
const {
isLoading,
signIn,
Expand All @@ -30,14 +28,10 @@ export const SetupPage: FC<{
if (!buildInfoQuery.data) {
return;
}
sendDeploymentEvent(
{
type: "deployment_setup",
deployment_id: buildInfoQuery.data.deployment_id,
},
telemetryURL,
);
}, [buildInfoQuery.data, telemetryURL]);
sendDeploymentEvent(buildInfoQuery.data, {
type: "deployment_setup",
});
}, [buildInfoQuery.data, metadata.telemetry.value]);

if (isLoading) {
return <Loader fullscreen />;
Expand Down
1 change: 1 addition & 0 deletions site/src/testHelpers/entities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,7 @@ export const MockBuildInfo: TypesGen.BuildInfoResponse = {
workspace_proxy: false,
upgrade_message: "My custom upgrade message",
deployment_id: "510d407f-e521-4180-b559-eab4a6d802b8",
telemetry: true,
};

export const MockSupportLinks: TypesGen.LinkConfig[] = [
Expand Down
31 changes: 23 additions & 8 deletions site/src/utils/telemetry.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,33 @@
import type { BuildInfoResponse } from "api/typesGenerated";

// sendDeploymentEvent sends a CORs payload to coder.com
// to track a deployment event.
export const sendDeploymentEvent = (payload: {
type: "deployment_setup" | "deployment_login";
deployment_id: string;
user_id?: string;
}) => {
export const sendDeploymentEvent = (
buildInfo: BuildInfoResponse,
payload: {
type: "deployment_setup" | "deployment_login";
user_id?: string;
},
) => {
if (typeof navigator === "undefined" || !navigator.sendBeacon) {
// It's fine if we don't report this, it's not required!
return;
}
if (!buildInfo.telemetry) {
return;
}
navigator.sendBeacon(
"https://coder.com/api/track-deployment",
new Blob([JSON.stringify(payload)], {
type: "application/json",
}),
new Blob(
[
JSON.stringify({
...payload,
deployment_id: buildInfo.deployment_id,
}),
],
{
type: "application/json",
},
),
);
};
Loading