feat(cli): gRPC/SSL/Traceroute typed result rendering + IaC constructs#1362
feat(cli): gRPC/SSL/Traceroute typed result rendering + IaC constructs#1362danielpaulus wants to merge 9 commits into
Conversation
37e7d34 to
1f8b58d
Compare
|
Follow-up scope identified after opening this PR: the current branch adds typed result/report rendering, but CLI IaC is not complete until the new monitor constructs exist. A remote implementation run has been kicked off to add:
Current context for that work:
Tracking ticket lives in the spec branch at |
Deploy validation evidence (local devenv, flags enabled)Target: local devenv backend ( What fully passed via the real CLIA fixture project with one Parse → validate → bundle of all three new constructs succeeds against the real backend. Known blocker for end-to-end
|
| type | POST | id | GET | DELETE | GET after delete |
|---|---|---|---|---|---|
| GRPC | 201 | 3181f60a-4640-461a-92d3-efad5a3d43a1 |
200 | 204 | 404 |
| SSL | 201 | afbf4313-5dbf-4d70-9f35-bc09ec3a6466 |
200 | 204 | 404 |
| TRACEROUTE | 201 | 1f11f58d-f797-45bb-9e33-e37ee7ca2ccc |
200 | 204 | 404 |
All three created checks were deleted afterwards (confirmed 404). Created checkType values matched GRPC/SSL/TRACEROUTE.
Payload parity
GrpcMonitor/SslMonitor/TracerouteMonitor .synthesize() output is byte-identical to the previously parity-validated create bodies (request block diff = empty for all three types).
Net: constructs parse/validate/bundle through the real CLI and the backend creates checks from their exact payloads. The only gap to a single npx checkly deploy run is the devenv's unavailable code-bundle storage endpoint, not the construct code.
|
Addendum: re-ran the CLI validation without Using checkly whoami
You are currently on account "dude@dude.com" (...) as undefined.
Plan: EnterpriseThen Parsing your project... ✅
Validating project resources... ✅
Bundling project resources... ✅
Uploading Playwright tests... ❌
ServerError: An internal server error occurred
Caused by: AxiosError: Request failed with status code 500So |
T65: local CLI deploy of GRPC/SSL/TRACEROUTE uptime monitorsPushed Problem: Fix (this PR): add Companion backend fix (checkly monorepo): the project-deploy path also silently dropped the type-specific config ( Verified end-to-end against devenv (real
Created checks cleaned up after verification. |
|
T65 update: CLI PR now includes the deploy fix for uptime-only projects. New commit: What changed:
UAT from hub ticket T65:
|
New-monitor checklist auditI compared this PR against the ✅ Covered
❌ Missing
|
…I-RESULTS) Render failure-debug diagnostics for the three uptime monitor types across every CLI result surface, mirroring the 4.1 public check-results fields: - rest/check-results.ts: typed TracerouteCheckResult/GrpcCheckResult/ SslCheckResult interfaces + additive fields on CheckResult/CheckResultField. - formatters/check-result-detail.ts: per-type terminal + markdown diagnostic block (checks results get) keyed on the typed fields. - reporters/util.ts: GRPC/SSL/TRACEROUTE branches in formatCheckResult (checkly test terminal), sourced from the runner artifact (checkRunData). - formatters/batch-stats.ts: add the three types to TIMING_TYPES. - reporters/json.ts: emit a per-type diagnostics object in the JSON report for a failed run (checkly test --reporter json). Snapshot + assertion tests for each type across all three surfaces. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…constructs Implement the missing CLI constructs so users can author and deploy gRPC, SSL and Traceroute uptime monitors via checkly/constructs, and so backend CLI export templates that import these classes compile. Constructs follow the DNS/TCP/ICMP monitor pattern (extend Monitor, register with Session, validate, synthesize): - GrpcMonitor / GrpcRequest / GrpcConfig / GrpcMetadata + GrpcAssertionBuilder (RESPONSE_TIME, GRPC_STATUS_CODE, GRPC_HEALTHCHECK_STATUS, GRPC_RESPONSE, GRPC_METADATA). checkType GRPC, top-level degraded/maxResponseTime (<=30000). - SslMonitor / SslRequest / SslConfig / SecurityBaseline + SslAssertionBuilder (cert expiry, chain, hostname, TLS version, cipher, key size, etc). checkType SSL; response-time limits live in sslConfig (degraded/maxResponseTimeMs). - TracerouteMonitor / TracerouteRequest + TracerouteAssertionBuilder (RESPONSE_TIME, HOP_COUNT, PACKET_LOSS). checkType TRACEROUTE, top-level degraded/maxResponseTime (<=30000); port dropped for ICMP probes. Request shapes mirror the public API (verified against checkly-go-sdk types and the p5-parity captured payloads). Wires exports in constructs/index.ts, import codegen in check-codegen.ts (GRPC/SSL/TRACEROUTE -> *MonitorCodegen), and adds the three types to constants.CheckTypes. Tests: construct synthesize/validation/grouping specs, codegen specs (incl. ICMP port-strip and assertion builders), and a regression spec asserting the backend-style export snippets compile against checkly/constructs. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
archive.store() (the 'Uploading Playwright tests' step) was called unconditionally on every deploy. For a project of only uptime monitors (GRPC/SSL/TRACEROUTE), the bundler registers no files, so this uploaded an empty Playwright bundle — an unnecessary code-bundle upload in production, and a hard failure in devenv where it 500s on the storage backend. The remote code bundle is consumed only by Playwright check suites (via bundler.marker -> playwright-check.ts); browser checks upload snapshots separately. Add Bundler.isEmpty and skip store() when no files were registered. Refs T65. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…r rendering Addresses review (Simo) new-monitor checklist gaps: - src/rest/analytics.ts: add checkTypeToPath (grpc-checks/ssl/traceroute) and defaultMetrics for the three types, matching the backend analytics routes + metric registries (gRPC total_*, SSL handshakeTimeMs_*/daysUntilExpiry_avg, TRACEROUTE finalHopLatencyAvg_*/totalHops_avg). - src/reporters/util.ts: add the Connection Error subsection to the GRPC/SSL/ TRACEROUTE result blocks, mirroring ICMP/DNS.
…onitors
Addresses review (Simo) new-monitor checklist Phases 5 & 7:
- ai-context/references/configure-{grpc,ssl,traceroute}-monitors.md + REFERENCES
and EXAMPLE_CONFIGS entries in context.ts (inline exampleConfig).
- examples/advanced-project{,-js}/src/__checks__/uptime/{grpc,ssl,traceroute}
TS + JS examples with group, assertions, and doc-link comments.
ea5e677 to
635288c
Compare
|
Thanks for the checklist audit @sorccu — addressed all of it. Pushed 3 commits (also rebased on ❌ Missing — now done
|
|
Correction on Phase 6 (e2e): I initially added the three fixtures to Proposal: add the deploy-project e2e fixtures once the three types are GA on production (or gate them behind a skip until then). The other checklist items (analytics, AI context, examples, connection-error) are unaffected and remain. End-to-end deploy/create/read/destroy for all three is already covered against the local devenv in the spec's P5 UAT. |
635288c to
8358b22
Compare
…route
Addresses review (Simo) new-monitor checklist Phase 6:
- e2e/__tests__/fixtures/deploy-project/{grpc,ssl,traceroute}.check.ts (activated:false)
- deploy.spec.ts: assert the three logical IDs in the Create output.
|
Re-instated the Phase 6 e2e fixtures + |
| * Unlike most monitors, the response-time limits for an SSL monitor live | ||
| * inside the request as `sslConfig.degradedResponseTimeMs` and | ||
| * `sslConfig.maxResponseTimeMs`. |
There was a problem hiding this comment.
But why? Why can't we have it the same way we have it for every other monitor and check?
| * @maximum 30000 | ||
| * @default 10000 | ||
| */ | ||
| handshakeTimeoutMs?: number |
There was a problem hiding this comment.
We don't traditionally include an Ms suffix for properties even if they are timeouts and even if the value is milliseconds. I would remove it.
| /** | ||
| * The handshake time in milliseconds above which the monitor is considered | ||
| * degraded. | ||
| * | ||
| * @minimum 0 | ||
| * @maximum 30000 | ||
| * @default 3000 | ||
| */ | ||
| degradedResponseTimeMs?: number | ||
|
|
||
| /** | ||
| * The handshake time in milliseconds above which the monitor is considered | ||
| * failing. Must be greater than or equal to `degradedResponseTimeMs`. | ||
| * | ||
| * @minimum 0 | ||
| * @maximum 30000 | ||
| * @default 10000 | ||
| */ | ||
| maxResponseTimeMs?: number |
There was a problem hiding this comment.
These don't belong here. In every other monitor/check, they are part of the check's properties. In the check properties, you have a comment saying exactly that. However, the reason is not explained and is therefore completely unclear to me.
| * The ID of the stored client certificate to present. Required when | ||
| * `sslConfig.clientCertificateMode` is `explicit`. | ||
| */ | ||
| sslClientCertificateId?: string |
There was a problem hiding this comment.
Why is this at the top level when everything else is inside sslConfig? If it's for model reasons - the request doesn't have to match the model exactly. You can remap things when you synthesize the bundle.
However, I feel like a few more properties that are directly related to forming the connection to the server should actually be OUTSIDE sslConfig. And if those are moved, then maybe this one should stay at the top level.
|
I have a bit of an issue with the way SslMonitor is structured. Here's what it would roughly look like almost populated: new SslMonitor('example-com-ssl', {
name: 'example.com SSL Certificate',
activated: true,
frequency: 60,
locations: ['eu-west-1'],
request: {
sslClientCertificateId: 'clientcert_1234', // required when mode is 'explicit'
sslConfig: {
hostname: 'example.com', // no scheme, no port
port: 443,
ipFamily: 'IPv6',
serverName: 'example.com', // defaults to hostname
alertDaysBeforeExpiry: 30,
degradedResponseTimeMs: 3000,
maxResponseTimeMs: 10000,
skipChainValidation: false,
// Override the account-default security baseline.
securityBaseline: {
enabled: true,
minTLSVersion: { value: 'TLS1.2', severity: 'fail' },
minKeySizeBits: { value: 2048, severity: 'fail' },
weakCipherSuite: { severity: 'warn' },
},
},
assertions: [
SslAssertionBuilder.certExpiresInDays().greaterThan(30),
SslAssertionBuilder.certNotExpired().equals(true),
SslAssertionBuilder.chainTrusted().equals(true),
SslAssertionBuilder.hostnameVerified().equals(true),
SslAssertionBuilder.tlsVersion().equals('TLS1.3'),
SslAssertionBuilder.keySizeBits().greaterThan(2048),
SslAssertionBuilder.issuerCn().contains("Let's Encrypt"),
],
},
})In my opinion here is a closer approximation of what it should look like: new SslMonitor('example-com-ssl', {
name: 'example.com SSL Certificate',
activated: true,
frequency: 60,
locations: ['eu-west-1'],
request: {
hostname: 'example.com', // no scheme, no port
port: 443,
ipFamily: 'IPv6',
sslConfig: {
serverName: 'example.com', // defaults to hostname
sslClientCertificateId: 'clientcert_1234', // required when mode is 'explicit'
alertDaysBeforeExpiry: 30,
skipChainValidation: false,
// Override the account-default security baseline.
securityBaseline: {
enabled: true,
minTLSVersion: { value: 'TLS1.2', severity: 'fail' },
minKeySizeBits: { value: 2048, severity: 'fail' },
weakCipherSuite: { severity: 'warn' },
},
},
assertions: [
SslAssertionBuilder.certExpiresInDays().greaterThan(30),
SslAssertionBuilder.certNotExpired().equals(true),
SslAssertionBuilder.chainTrusted().equals(true),
SslAssertionBuilder.hostnameVerified().equals(true),
SslAssertionBuilder.tlsVersion().equals('TLS1.3'),
SslAssertionBuilder.keySizeBits().greaterThan(2048),
SslAssertionBuilder.issuerCn().contains("Let's Encrypt"),
],
},
degradedResponseTime: 3000,
maxResponseTime: 10000,
})This moves |
Mirror monorepo #2730 which added TEXT_BODY to grpcMonitorAssertionSources. Adds GrpcAssertionBuilder.textBody() (source TEXT_BODY) + codegen case, matching the API check's textBody() builder.
…changed) Per @sorccu's review: make SslMonitor ergonomics consistent with other monitors. - degradedResponseTime/maxResponseTime -> construct top level (were in sslConfig) - hostname/port/ipFamily -> request top level (were in sslConfig) - sslClientCertificateId -> into sslConfig (was on request) synthesize() remaps to the identical API wire shape (request.sslConfig.* + request.sslClientCertificateId), so the payload the backend receives is unchanged. Updated codegen (wire->construct), unit + codegen + export-snippet tests, examples (ts+js), e2e fixture, and AI context.
|
Done — restructured
Also folded in monorepo #2730: added Follow-up I'll handle separately: the backend |
@sorccu: we don't suffix timeout properties with Ms even when the value is milliseconds. Renames the construct field handshakeTimeoutMs -> handshakeTimeout; synthesize() and the codegen wire interface keep the API's handshakeTimeoutMs.
|
Also dropped the |
Summary
This branch now covers two related pieces of CLI support for the new uptime monitor types (gRPC, SSL, Traceroute):
1. Typed result/report rendering (original scope)
2. IaC constructs (added)
Implements the missing
checkly/constructsclasses so users can author and deploy the new monitor types, and so backend CLI export templates thatimport { GrpcMonitor, SslMonitor, TracerouteMonitor } from 'checkly/constructs'compile.GrpcMonitor(grpc-monitor.ts,grpc-request.ts,grpc-assertion.ts+ codegen) —checkType: 'GRPC', top-leveldegradedResponseTime/maxResponseTime(≤30000). Request mirrors the public API:url,port,ipFamily,skipSSL,timeout,grpcConfig { mode, tls, storeResponseBody, serviceDefinition, method, protoContent, message, service, metadata },assertions.GrpcAssertionBuilder:responseTime,statusCode,healthCheckStatus,responseMessage,responseMetadata.SslMonitor(ssl-monitor.ts,ssl-request.ts,ssl-assertion.ts+ codegen) —checkType: 'SSL'. Response-time limits live insidesslConfig(degradedResponseTimeMs/maxResponseTimeMs), not at the top level. ModelsserverName,skipChainValidation,handshakeTimeoutMs,alertDaysBeforeExpiry,securityBaseline,clientCertificateMode+sslClientCertificateId.SslAssertionBuildercovers cert expiry/trust/hostname/TLS version/cipher/key size/etc.TracerouteMonitor(traceroute-monitor.ts,traceroute-request.ts,traceroute-assertion.ts+ codegen) —checkType: 'TRACEROUTE', top-leveldegradedResponseTime/maxResponseTime(≤30000). Request:url,protocol,port,ipFamily,maxHops,maxUnknownHops,ptrLookup,timeout,assertions.portis dropped from generated code forICMPprobes (matches the backend strip).TracerouteAssertionBuilder:responseTime,hopCount,packetLoss.Wiring:
constructs/index.ts.constructs/check-codegen.ts(GRPC → GrpcMonitorCodegen,SSL → SslMonitorCodegen,TRACEROUTE → TracerouteMonitorCodegen).GRPC/SSL/TRACEROUTEadded toconstants.CheckTypes.Request shapes were verified against
checkly-go-sdktypes and the terraform-provider resource schemas, and the synthesized payloads are byte-identical to the previously parity-validated create bodies.3. Deploy fix for uptime-only projects (T65)
Bundler.isEmptyand skips thearchive.store()/ "Uploading Playwright tests" step when no files were registered for bundling./next/checkly-storage/upload-code-bundle.Feature flags for the backend:
GRPC_CHECKS,SSL_CHECKS,TRACEROUTE_CHECKS.Testing
npx checkly deployagainst local devenv through the dev-auth proxy created one GRPC, one SSL, and one TRACEROUTE check withoutCHECKLY_SKIP_AUTH; the uptime-only project skipped "Uploading Playwright tests"; checks were readable via the public API and cleaned up.tsc --noEmitandtsc --build: clean.eslinton all new/changed files: clean.constructs/__tests__/grpc-monitor.spec.ts,ssl-monitor.spec.ts,traceroute-monitor.spec.ts— synthesize / validation / grouping.constructs/__tests__/uptime-monitor-codegen.spec.ts— codegen emits compiling construct code (incl. ICMPportstrip, assertion builders).constructs/__tests__/uptime-monitor-export-snippets.spec.ts— backend-style export snippets compile againstcheckly/constructsand synthesize the expected payload.