Skip to content

Add endpoint preflight checks - #13

Open
ismith wants to merge 9 commits into
mainfrom
ismith/endpoint-preflight-checks
Open

ismith wants to merge 9 commits into
mainfrom
ismith/endpoint-preflight-checks

Conversation

@ismith

@ismith ismith commented Sep 9, 2026

Copy link
Copy Markdown

Summary

  • add gh elm config check to report source and target network reachability, API service health, and authentication independently
  • run the same source preflight before every source-backed gh elm migration command
  • write diagnostics, including successful checks, to stderr so normal and JSON stdout remain clean

Why

While preparing a support escalation drill, a gh elm migration status request returned a 404 without enough context to distinguish an unreachable source, an unhealthy API, rejected credentials, unavailable ELM endpoints, or an unknown migration.

These checks make configuration failures visible before migration operations and provide reusable diagnostics for users and support responders.

Behavior

  • source (GHES) uses the normalized GHES authenticated-user endpoint (/api/v3/user); the target (GitHub with Data Residency) uses /user
  • HTTP responses prove network reachability, while 5xx responses are reported separately as unhealthy service responses
  • 401 and 403 responses are reported as authentication failures with token-safe guidance
  • gh elm config check reports both endpoints even when one fails
  • source-backed migration operations stop when source preflight fails
  • local argument validation still runs before network requests

Testing

  • make audit
  • Ran locally; saw both failures (transient TLS handshake timeouts) and successes:
Local tests

Test 1

$ gh elm config check
[OK] Source network: reachable (HTTP 200 OK)
[OK] Source service: responding (HTTP 200 OK)
[OK] Source authentication: credentials accepted by https://[ghes-host]/api/v3/user
[OK] Target network: reachable (HTTP 200 OK)
[OK] Target service: responding (HTTP 200 OK)
[OK] Target authentication: credentials accepted by https://api.[target-host]/user

Test 2

$ gh elm migration status <migration-uuid>
[FAIL] Source network: checking authentication: sending request: Get "https://[ghes-host]/api/v3/user": net/http: TLS handshake timeout
[FAIL] Source service: not checked because the network probe failed
[FAIL] Source authentication: could not verify credentials: checking authentication: sending request: Get "https://[ghes-host]/api/v3/user": net/http: TLS handshake timeout
Error
source preflight failed; see checks above

$ gh elm migration status <migration-uuid>
[OK] Source network: reachable (HTTP 200 OK)
[OK] Source service: responding (HTTP 200 OK)
[OK] Source authentication: credentials accepted by https://[ghes-host]/api/v3/user
acme/test → acme-[redacted]/test-oghhes1

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Redirect handling can falsely accept authentication, and diagnostic URLs may expose embedded credentials.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review tier: Balanced
Findings: 2 High severity

New issues introduced by this change (2)
Severity Finding
High severity internal/​preflight/​preflight.go — This endpoint is later written verbatim for successful and forbidden authentication checks. Since…
High severity internal/​preflight/​preflight.goCheckAuthentication uses the default http.Client, which follows redirects. A proxy or appliance…
What changed in this PR

Adds reusable endpoint diagnostics and applies source preflight checks before migration operations.

Changes:

  • Adds gh elm config check.
  • Integrates source checks across migration commands.
  • Adds documentation and automated coverage.
File Description
README.md Documents preflight behavior.
internal/​preflight/​preflight.go Implements endpoint checks.
internal/​preflight/​preflight_test.go Tests failure diagnostics.
internal/​cmd/​root_test.go Verifies command help.
internal/​cmd/​migration/​watch.go Adds watch preflight.
internal/​cmd/​migration/​migration.go Adds migration preflight.
internal/​cmd/​migration/​migration_test.go Updates migration tests.
internal/​cmd/​configure.go Registers config check.
internal/​cmd/​configure_test.go Tests config check.
internal/​cmd/​config_check.go Implements config check command.
integration/​cli_test.go Covers CLI preflight behavior.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread internal/preflight/preflight.go
Comment thread internal/preflight/preflight.go
Co-authored-by: ismith <172694+ismith@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: ismith <172694+ismith@users.noreply.github.com>
Co-authored-by: ismith <172694+ismith@users.noreply.github.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

The new preflight implementation contains a confirmed gofmt violation that must be corrected.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review tier: Balanced
Findings: 1 High severity

New issues introduced by this change (1)
Severity Finding
High severity internal/​preflight/​preflight.go — This line is not gofmt-formatted, so the repository's formatting/lint checks will reject or…
Issues resolved since last review (2)
Severity Finding
High severity internal/​preflight/​preflight.goCheckAuthentication uses the default http.Client, which follows redirects. A proxy or appliance… View resolved comment
High severity internal/​preflight/​preflight.go — This endpoint is later written verbatim for successful and forbidden authentication checks. Since… View resolved comment

Comment thread internal/preflight/preflight.go Outdated

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟢 Approval recommended

The implementation matches the documented behavior and includes appropriate unit and integration coverage.

Review tier: Balanced
Findings: None

Co-authored-by: ismith <172694+ismith@users.noreply.github.com>
@ismith
ismith requested a balanced review from Copilot September 9, 2026 20:46
@ismith
ismith marked this pull request as ready for review September 9, 2026 20:47
@ismith
ismith requested a review from a team as a code owner September 9, 2026 20:47

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

Resolver initialization failures currently prevent reporting either endpoint, contrary to the command’s stated behavior.

Review tier: Balanced
Findings: None

Suppressed comments (1)

Previously missed (1) — in code that hasn't changed since the last review.

internal/cmd/config_check.go:25

  • A malformed/unreadable config or invalid credential-store setting makes NewResolver fail here, so the command exits without emitting any Source or Target check results. This bypasses the stated guarantee that config check reports both endpoints; route this shared failure through the existing per-endpoint diagnostic helper instead.

…ostics

Report resolver failures for both endpoints

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟢 Approval recommended

The implementation matches the described behavior, with only a minor test-organization follow-up.

Review tier: Balanced
Findings: 1 Low severity

New issues introduced by this change (1)
Severity Finding
Low severity internal/​cmd/​config_check_test.go — Please move these resolver-failure cases under the existing TestConfigCheck as subtests. The same…

Comment thread internal/cmd/config_check_test.go Outdated
Co-authored-by: ismith <172694+ismith@users.noreply.github.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟢 Approval recommended

The preflight behavior, redirect handling, diagnostics, and migration integration are consistently implemented and tested.

Review tier: Balanced
Findings: 1 Low severity

Pre-existing issues (1)
Severity Finding
Low severity internal/​cmd/​config_check_test.go — Please move these resolver-failure cases under the existing TestConfigCheck as subtests. The same… View comment

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟢 Approval recommended

The implementation and tests consistently satisfy the documented diagnostic, authentication, and output-stream requirements.

Review tier: Balanced
Findings: 1 Low severity

Pre-existing issues (1)
Severity Finding
Low severity internal/​cmd/​config_check_test.go — Please move these resolver-failure cases under the existing TestConfigCheck as subtests. The same… View comment

@dpmex4527 dpmex4527 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These preflight checks are a good start, but I think the current validation is a bit too optimistic. A successful /user probe only confirms that the token is accepted by the GHES instance; it does not validate the token scopes required for ELM APIs (admin:enterprise) or confirm that the ELM service itself is available/enabled.

I’d recommend treating this as a minimum connectivity/auth check, not a full ELM readiness check. As a next step, we should validate the actual ELM API path, e.g. GET /api/v3/enterprise/live-migrations?page_size=1, and surface explicit authorization/service failures there so users get a clear signal when the token is valid for the instance but not authorized for ELM.

I also left a few suggestions regarding UX for enabling customers to opt out of preflight checks if they need to.

// CheckEndpoint reports network reachability, API service health, and
// authentication independently. It runs every applicable check even after a
// failure so the output is useful for diagnosing configuration problems.
func CheckEndpoint(ctx context.Context, stderr io.Writer, name, baseURL, token string) error {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we make successful diagnostics configurable here, rather than always writing them? sourceClient is used by commands that support --json, and emitting [OK] lines to stderr changes the observable output for integrations that capture or merge both streams. I suggest keeping all diagnostics for gh elm config check, but suppressing successful checks for migration commands when --json is set while continuing to emit failures so users still get actionable context.

Some options to consider

  • Only display success diagnostics when a --verbose mode flag is being used. Keep failed preflight checks being sent to stderr for all commands on failure
  • Alternatively add a --quiet flag that suppresses diagnostic output. I'm leaning more towards having success preflight checks go through --verbose though.


// sourceFlags registers the shared --source-url/--source-token overrides on a
// command. The flag values are read back via cmd.Flags().GetString at call time.
func sourceFlags(cmd *cobra.Command) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be useful to add a shared --no-preflight flag here?

Some customers may not want the CLI to perform an additional authenticated /user request before every source-backed command, especially polling commands such as status, watch, or repeated list calls. They may already perform connectivity and authentication checks externally, or may want to minimize request volume and latency. Providing an explicit opt-out would make that behavior configurable without weakening the safer default.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants