Skip to content

Commit 03c78cc

Browse files
juliusmarmingecodex
andcommitted
Trace managed relay connection bootstrap
Co-authored-by: codex <codex@users.noreply.github.com>
1 parent b200c9d commit 03c78cc

10 files changed

Lines changed: 153 additions & 43 deletions

File tree

apps/server/src/cli/cloud.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ import * as CliState from "../cloud/CliState.ts";
3030
import * as CliTokenManager from "../cloud/CliTokenManager.ts";
3131
import { CLOUD_LINKED_USER_ID, RELAY_URL_SECRET } from "../cloud/config.ts";
3232
import { relayUrlConfig } from "../cloud/publicConfig.ts";
33-
import { serverRelayClientTracingLayer } from "../cloud/relayTracing.ts";
33+
import { headlessRelayClientTracingLayer } from "../cloud/relayTracing.ts";
3434
import { ServerConfig } from "../config.ts";
3535
import { ServerEnvironmentLive } from "../environment/Layers/ServerEnvironment.ts";
3636
import { ServerEnvironment } from "../environment/Services/ServerEnvironment.ts";
@@ -302,7 +302,7 @@ const runCloudCommand = <A, E>(
302302
RelayClient.layerCloudflared({ baseDir: config.baseDir }),
303303
EnvironmentAuth.runtimeLayer,
304304
ServerEnvironmentLive,
305-
serverRelayClientTracingLayer,
305+
headlessRelayClientTracingLayer,
306306
).pipe(
307307
Layer.provideMerge(FetchHttpClient.layer),
308308
Layer.provideMerge(Layer.succeed(ServerConfig, config)),

apps/server/src/cloud/http.test.ts

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,19 @@ import { describe, expect, it } from "@effect/vitest";
33
import * as Effect from "effect/Effect";
44
import * as Option from "effect/Option";
55
import * as PlatformError from "effect/PlatformError";
6-
import { HttpClient } from "effect/unstable/http";
6+
import * as Tracer from "effect/Tracer";
7+
import { HttpClient, HttpServerRequest } from "effect/unstable/http";
78

9+
import { RelayClientTracer } from "@t3tools/shared/relayTracing";
810
import * as EnvironmentAuth from "../auth/EnvironmentAuth.ts";
911
import * as ServerSecretStore from "../auth/ServerSecretStore.ts";
1012
import { ServerEnvironment } from "../environment/Services/ServerEnvironment.ts";
1113
import * as CliTokenManager from "./CliTokenManager.ts";
12-
import { consumeCloudReplayGuards, reconcileDesiredCloudLink } from "./http.ts";
14+
import {
15+
consumeCloudReplayGuards,
16+
reconcileDesiredCloudLink,
17+
traceRelayBrokerHandler,
18+
} from "./http.ts";
1319
import {
1420
CloudManagedEndpointRuntime,
1521
type CloudManagedEndpointRuntimeShape,
@@ -69,6 +75,38 @@ describe("consumeCloudReplayGuards", () => {
6975
);
7076
});
7177

78+
describe("traceRelayBrokerHandler", () => {
79+
it.effect("continues the incoming relay trace with the product tracer", () =>
80+
Effect.gen(function* () {
81+
const spans: Array<Tracer.Span> = [];
82+
const productTracer = Tracer.make({
83+
span: (options) => {
84+
const span = new Tracer.NativeSpan(options);
85+
spans.push(span);
86+
return span;
87+
},
88+
});
89+
const request = HttpServerRequest.fromWeb(
90+
new Request("https://environment.example.test/api/t3-cloud/mint-credential", {
91+
headers: {
92+
traceparent: "00-0123456789abcdef0123456789abcdef-0123456789abcdef-01",
93+
},
94+
}),
95+
);
96+
97+
yield* traceRelayBrokerHandler(Effect.void.pipe(Effect.withSpan("relay.mint.handler"))).pipe(
98+
Effect.provideService(HttpServerRequest.HttpServerRequest, request),
99+
Effect.provideService(RelayClientTracer, Option.some(productTracer)),
100+
);
101+
102+
expect(spans).toHaveLength(1);
103+
const span = spans[0]!;
104+
expect(span.traceId).toBe("0123456789abcdef0123456789abcdef");
105+
expect(Option.getOrUndefined(span.parent)?.spanId).toBe("0123456789abcdef");
106+
}),
107+
);
108+
});
109+
72110
describe("reconcileDesiredCloudLink", () => {
73111
it.effect("requires stored CLI authorization without exposing an HTTP endpoint", () =>
74112
Effect.gen(function* () {

apps/server/src/cloud/http.ts

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ import * as Effect from "effect/Effect";
4848
import * as Option from "effect/Option";
4949
import * as Schema from "effect/Schema";
5050
import * as HttpEffect from "effect/unstable/http/HttpEffect";
51-
import { HttpServerRequest, HttpServerResponse } from "effect/unstable/http";
51+
import { HttpServerRequest, HttpServerResponse, HttpTraceContext } from "effect/unstable/http";
5252
import { HttpClient, HttpClientRequest, HttpClientResponse } from "effect/unstable/http";
5353
import * as HttpApiBuilder from "effect/unstable/httpapi/HttpApiBuilder";
5454

@@ -111,6 +111,19 @@ const requireRelayUrl = relayUrlConfig.pipe(
111111
),
112112
);
113113

114+
export const traceRelayBrokerHandler = <A, E, R>(
115+
effect: Effect.Effect<A, E, R>,
116+
): Effect.Effect<A, E, R | HttpServerRequest.HttpServerRequest> =>
117+
HttpServerRequest.HttpServerRequest.pipe(
118+
Effect.flatMap((request) =>
119+
Option.match(HttpTraceContext.fromHeaders(request.headers), {
120+
onNone: () => effect,
121+
onSome: (parent) => effect.pipe(Effect.withParentSpan(parent)),
122+
}),
123+
),
124+
withRelayClientTracing,
125+
);
126+
114127
function bytesToString(bytes: Uint8Array): string {
115128
return new TextDecoder().decode(bytes);
116129
}
@@ -940,7 +953,7 @@ export const cloudHttpApiLayer = HttpApiBuilder.group(
940953
.handle("health", ({ payload }) => cloudEnvironmentHealthHandler(dependencies, payload))
941954
.handle("mintCredential", ({ payload }) => cloudMintCredentialHandler(dependencies, payload))
942955
.handle("t3MintCredential", ({ payload }) =>
943-
cloudMintCredentialHandler(dependencies, payload),
956+
traceRelayBrokerHandler(cloudMintCredentialHandler(dependencies, payload)),
944957
);
945958
}),
946959
);

apps/server/src/cloud/relayTracing.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,20 @@ import { makeRelayClientTracingLayer } from "@t3tools/shared/relayTracing";
22

33
import { resolveRelayClientTracingConfig } from "./publicConfig.ts";
44

5-
export const serverRelayClientTracingLayer = makeRelayClientTracingLayer(
6-
resolveRelayClientTracingConfig(),
5+
const relayClientTracingConfig = resolveRelayClientTracingConfig();
6+
7+
export const headlessRelayClientTracingLayer = makeRelayClientTracingLayer(
8+
relayClientTracingConfig,
79
{
810
serviceName: "t3-headless-relay-client",
911
runtime: "node",
1012
client: "headless-cli",
1113
},
1214
);
15+
16+
export const serverRelayBrokerTracingLayer = makeRelayClientTracingLayer(relayClientTracingConfig, {
17+
serviceName: "t3-server",
18+
runtime: "node",
19+
client: "environment-server",
20+
component: "relay-broker",
21+
});

apps/server/src/server.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ import { authHttpApiLayer, environmentAuthenticatedAuthLayer } from "./auth/http
7070
import * as ServerSecretStore from "./auth/ServerSecretStore.ts";
7171
import * as EnvironmentAuth from "./auth/EnvironmentAuth.ts";
7272
import { cloudHttpApiLayer, reconcileDesiredCloudLink } from "./cloud/http.ts";
73-
import { serverRelayClientTracingLayer } from "./cloud/relayTracing.ts";
73+
import { serverRelayBrokerTracingLayer } from "./cloud/relayTracing.ts";
7474
import * as CloudManagedEndpointRuntime from "./cloud/ManagedEndpointRuntime.ts";
7575
import * as CloudCliTokenManager from "./cloud/CliTokenManager.ts";
7676
import * as CloudCliState from "./cloud/CliState.ts";
@@ -453,7 +453,7 @@ export const makeServerLayer = Layer.unwrap(
453453

454454
return serverApplicationLayer.pipe(
455455
Layer.provideMerge(RuntimeServicesLive),
456-
Layer.provideMerge(serverRelayClientTracingLayer),
456+
Layer.provideMerge(serverRelayBrokerTracingLayer),
457457
Layer.provideMerge(HttpServerLive),
458458
Layer.provide(ObservabilityLive),
459459
Layer.provideMerge(FetchHttpClient.layer),

apps/web/src/cloud/linkEnvironment.test.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,14 @@ describe("web cloud link environment client", () => {
318318
method: "POST",
319319
url: "https://managed.example.test/oauth/token",
320320
});
321+
const traceparents = fetchMock.mock.calls.map(
322+
(call) => call[1]?.headers.traceparent as string | undefined,
323+
);
324+
expect(traceparents.every((traceparent) => typeof traceparent === "string")).toBe(true);
325+
expect(new Set(traceparents.map((traceparent) => traceparent?.split("-")[1])).size).toBe(1);
326+
expect(connection.relayTraceHeaders.traceparent?.split("-")[1]).toBe(
327+
traceparents[0]?.split("-")[1],
328+
);
321329
}),
322330
);
323331

apps/web/src/cloud/linkEnvironment.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import * as Data from "effect/Data";
22
import * as Effect from "effect/Effect";
33
import * as Schema from "effect/Schema";
4-
import { HttpClient } from "effect/unstable/http";
4+
import { HttpClient, HttpTraceContext, type Headers } from "effect/unstable/http";
55
import {
66
EnvironmentCloudEndpointUnavailableError,
77
type EnvironmentCloudLinkStateResult,
@@ -29,6 +29,7 @@ import {
2929
ManagedRelayDpopSigner,
3030
type WsRpcClient,
3131
} from "@t3tools/client-runtime";
32+
import { withRelayClientTracing } from "@t3tools/shared/relayTracing";
3233

3334
import { ensureLocalApi } from "../localApi";
3435
import {
@@ -245,6 +246,7 @@ export interface CloudManagedConnection {
245246
readonly wsBaseUrl: string;
246247
readonly relayUrl: string;
247248
readonly accessToken: string;
249+
readonly relayTraceHeaders: Headers.Headers;
248250
}
249251

250252
export function collectCloudLinkTargets(input: {
@@ -437,8 +439,15 @@ export function connectManagedCloudEnvironment(input: {
437439
wsBaseUrl: connected.endpoint.wsBaseUrl,
438440
relayUrl: configuredRelayUrl,
439441
accessToken: session.access_token,
442+
relayTraceHeaders: HttpTraceContext.toHeaders(yield* Effect.currentSpan.pipe(Effect.orDie)),
440443
};
441-
});
444+
}).pipe(
445+
Effect.withSpan("relay.environment.connect", {
446+
root: true,
447+
attributes: { "relay.environment_id": input.environment.environmentId },
448+
}),
449+
withRelayClientTracing,
450+
);
442451
}
443452

444453
export function readPrimaryCloudLinkState(): Effect.Effect<

apps/web/src/environments/runtime/service.addSavedEnvironment.test.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { EnvironmentAuthInvalidError, EnvironmentId } from "@t3tools/contracts";
22
import * as Effect from "effect/Effect";
33
import * as Schema from "effect/Schema";
4+
import { Headers } from "effect/unstable/http";
45
import { afterEach, beforeEach, describe, expect, it, vi } from "vite-plus/test";
56

67
const decodeEnvironmentAuthInvalidError = Schema.decodeUnknownSync(EnvironmentAuthInvalidError);
@@ -383,6 +384,7 @@ describe("addSavedEnvironment", () => {
383384
wsBaseUrl: "wss://managed.example.com/",
384385
relayUrl: "https://relay.example.com",
385386
accessToken: "managed-access-token",
387+
relayTraceHeaders: Headers.empty,
386388
});
387389

388390
expect(mockWriteSavedEnvironmentCredential).toHaveBeenCalledWith(
@@ -440,6 +442,7 @@ describe("addSavedEnvironment", () => {
440442
wsBaseUrl: "wss://managed.example.com/",
441443
relayUrl: "https://relay.example.com",
442444
accessToken: "renewed-access-token",
445+
relayTraceHeaders: Headers.empty,
443446
}),
444447
);
445448

0 commit comments

Comments
 (0)