Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
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
22 changes: 13 additions & 9 deletions apps/api/src/routes/internal/query-engine.http.ts
Original file line number Diff line number Diff line change
Expand Up @@ -254,17 +254,21 @@ const toServicePlatformRow = (row: CH.ServicePlatformsOutput) => {
const processRuntimeName = row.processRuntimeName
// cluster.name alone does not prove the service runs in Kubernetes.
const isKubernetes = k8sPodName !== "" || k8sDeploymentName !== ""
// Host infrastructure takes precedence over SDK self-report.
const platform: "kubernetes" | "cloudflare" | "lambda" | "web" | "unknown" =
// Host infrastructure takes precedence over SDK self-report. Google Cloud's
// detector sets `faas.name` on Cloud Run and Cloud Functions, so it is checked
// before the Lambda rule.
const platform: "kubernetes" | "cloudflare" | "gcp" | "lambda" | "web" | "unknown" =
cloudPlatform === "cloudflare.workers" || cloudProvider === "cloudflare"
? "cloudflare"
: faasName !== "" || cloudPlatform === "aws_lambda"
? "lambda"
: isKubernetes
? "kubernetes"
: mapleSdkType === "client"
? "web"
: "unknown"
: cloudProvider === "gcp"
? "gcp"
: faasName !== "" || cloudPlatform === "aws_lambda"
? "lambda"
: isKubernetes
? "kubernetes"
: mapleSdkType === "client"
? "web"
: "unknown"
return {
serviceName: decodeServiceName(row.serviceName),
platform,
Expand Down
15 changes: 15 additions & 0 deletions apps/local-ui/src/hooks/use-local-service-map.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,21 @@ describe("classifyPlatform", () => {
"cloudflare",
)
expect(classifyPlatform(platformRow({ faasName: "fn", k8sPodName: "pod" }))).toBe("lambda")
// Cloud Run and GKE: Google Cloud's detector also sets `faas.name` / the k8s attributes.
expect(
classifyPlatform(
platformRow({ cloudProvider: "gcp", cloudPlatform: "gcp_cloud_run", faasName: "checkout" }),
),
).toBe("gcp")
expect(
classifyPlatform(
platformRow({
cloudProvider: "gcp",
cloudPlatform: "gcp_kubernetes_engine",
k8sPodName: "pod",
}),
),
).toBe("gcp")
expect(classifyPlatform(platformRow({ k8sDeploymentName: "api" }))).toBe("kubernetes")
expect(classifyPlatform(platformRow({ mapleSdkType: "client" }))).toBe("web")
})
Expand Down
2 changes: 2 additions & 0 deletions apps/local-ui/src/hooks/use-local-service-map.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,8 @@ function toDbEdge(row: CH.ServiceDbEdgesOutput, durationSeconds: number): Servic
/** Same precedence as the cloud API's platform classifier: host infrastructure beats SDK self-report. */
export function classifyPlatform(row: CH.ServicePlatformsOutput): ServicePlatform {
if (row.cloudPlatform === "cloudflare.workers" || row.cloudProvider === "cloudflare") return "cloudflare"
// Before the Lambda rule: Google Cloud's detector sets `faas.name` on Cloud Run and Cloud Functions.
if (row.cloudProvider === "gcp") return "gcp"
if (row.faasName !== "" || row.cloudPlatform === "aws_lambda") return "lambda"
// `k8s.cluster.name` alone does not prove the service runs in Kubernetes.
if (row.k8sPodName !== "" || row.k8sDeploymentName !== "") return "kubernetes"
Expand Down
2 changes: 1 addition & 1 deletion apps/web/src/api/warehouse/service-map.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ export interface ServiceDbQuerySummaryResponse {
topQueries: ReadonlyArray<ServiceDbTopQuery>
}

export type ServicePlatform = "kubernetes" | "cloudflare" | "lambda" | "web" | "unknown"
export type ServicePlatform = "kubernetes" | "cloudflare" | "gcp" | "lambda" | "web" | "unknown"

const GetServiceMapInputSchema = Schema.Struct({
startTime: Schema.optional(WarehouseDateTimeString),
Expand Down
3 changes: 2 additions & 1 deletion apps/web/src/components/icons/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ export {
FolderOpenIcon,
GlobeIcon,
GlobePointerIcon,
GoogleCloudIcon,
GoogleCloudMonoIcon,
KubernetesIcon,
LineHeightIcon,
LinkIcon,
Expand Down Expand Up @@ -137,7 +139,6 @@ export { GearIcon } from "./gear"
export { GeminiIcon } from "./gemini"
export { GithubIcon } from "./github"
export { GoogleIcon } from "./google"
export { GoogleCloudIcon, GoogleCloudMonoIcon } from "./google-cloud"
export { GrokIcon } from "./grok"
export { HaystackIcon } from "./haystack"
export { HuggingFaceIcon } from "./huggingface"
Expand Down
2 changes: 1 addition & 1 deletion apps/web/src/lab/bench/service-map-bench.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ function makeRng(seed: number): () => number {
}
}

const PLATFORMS: ServicePlatform[] = ["kubernetes", "cloudflare", "lambda", "web", "unknown"]
const PLATFORMS: ServicePlatform[] = ["kubernetes", "cloudflare", "gcp", "lambda", "web", "unknown"]
const RUNTIMES = ["nodejs", "bun", "deno", "edge-light", "workerd"]
const DB_SYSTEMS = ["postgresql", "mysql", "clickhouse", "redis", "mongodb"]

Expand Down
9 changes: 8 additions & 1 deletion packages/domain/src/http/query-engine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -997,7 +997,14 @@ export class ServiceDbQuerySummaryResponse extends Schema.Class<ServiceDbQuerySu
topQueries: Schema.Array(ServiceDbTopQuery),
}) {}

const ServicePlatformLiteral = Schema.Literals(["kubernetes", "cloudflare", "lambda", "web", "unknown"])
const ServicePlatformLiteral = Schema.Literals([
"kubernetes",
"cloudflare",
"gcp",
"lambda",
"web",
"unknown",
])

export class ServicePlatformsRequest extends Schema.Class<ServicePlatformsRequest>("ServicePlatformsRequest")(
{
Expand Down
1 change: 1 addition & 0 deletions packages/ui/src/components/icons/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ export { FireIcon } from "./fire"
export { FolderOpenIcon } from "./folder-open"
export { GaugeIcon } from "./gauge"
export { GlobeIcon } from "./globe"
export { GoogleCloudIcon, GoogleCloudMonoIcon } from "./google-cloud"
export { GlobePointerIcon } from "./globe-pointer"
export { IndustryIcon } from "./industry"
export { KubernetesIcon } from "./kubernetes"
Expand Down
13 changes: 10 additions & 3 deletions packages/ui/src/components/service-map/service-map-canvas.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -974,7 +974,14 @@ export function ServiceMapFlowCanvas({
)
}

const LEGEND_PLATFORMS = ["kubernetes", "cloudflare", "lambda", "web", "unknown"] as const
const LEGEND_PLATFORMS = [
["kubernetes", "Kubernetes"],
["cloudflare", "Cloudflare"],
["gcp", "Google Cloud"],
["lambda", "Lambda"],
["web", "Web"],
["unknown", "Unknown"],
] as const

const serviceSwatchColor = (service: string) =>
getServiceMapNodeColor({ label: service, kind: "service", errorRate: 0 }, "service")
Expand Down Expand Up @@ -1025,13 +1032,13 @@ function ServiceMapLegend({ colorMode, services }: { colorMode: ServiceMapColorM
{colorMode === "platform" && (
<>
<span className="text-foreground/30">|</span>
{LEGEND_PLATFORMS.map((p) => (
{LEGEND_PLATFORMS.map(([p, label]) => (
<div key={p} className="flex items-center gap-1.5">
<div
className="size-2.5 rounded-sm shrink-0"
style={{ backgroundColor: getPlatformColor(p === "unknown" ? undefined : p) }}
/>
<span className="font-medium capitalize">{p}</span>
<span className="font-medium">{label}</span>
</div>
))}
</>
Expand Down
3 changes: 3 additions & 0 deletions packages/ui/src/components/service-map/service-map-node.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
CloudflareBrandIcon,
CubeIcon,
GlobeIcon,
GoogleCloudIcon,
type IconComponent,
KubernetesIcon,
ServerIcon,
Expand All @@ -34,6 +35,8 @@ function getPlatformIcon(platform: ServicePlatform | undefined): {
return { Icon: KubernetesIcon, label: "Kubernetes", branded: true }
case "cloudflare":
return { Icon: CloudflareBrandIcon, label: "Cloudflare Workers", branded: true }
case "gcp":
return { Icon: GoogleCloudIcon, label: "Google Cloud", branded: true }
case "lambda":
return { Icon: AwsLambdaIcon, label: "AWS Lambda", branded: true }
case "web":
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// Local) maps its own query results onto these shapes, so the map itself never
// learns which backend answered.

export type ServicePlatform = "kubernetes" | "cloudflare" | "lambda" | "web" | "unknown"
export type ServicePlatform = "kubernetes" | "cloudflare" | "gcp" | "lambda" | "web" | "unknown"

/** One service-to-service edge over the window. */
export interface ServiceMapEdgeRow {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ export interface ServiceNodeData {
const PLATFORM_COLORS: Record<ServicePlatform | "unknown", string> = {
kubernetes: "oklch(0.62 0.16 250)",
cloudflare: CLOUDFLARE_COLOR,
gcp: "oklch(0.63 0.2 27)",
lambda: "oklch(0.7 0.18 60)",
web: "oklch(0.65 0.15 145)",
unknown: "oklch(0.55 0.02 270)",
Expand Down
Loading