Skip to content

Extract geoIP and mail service initialization out of runServeCmd - #47151

Merged
MagnusHJensen merged 1 commit into
fleetdm:mainfrom
raju249:33370-geoip-mail-extraction
Jun 9, 2026
Merged

Extract geoIP and mail service initialization out of runServeCmd#47151
MagnusHJensen merged 1 commit into
fleetdm:mainfrom
raju249:33370-geoip-mail-extraction

Conversation

@raju249

@raju249 raju249 commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

Extracts the geoIP provider and mail service setup out of runServeCmd and into new cmd/fleet/geoip.go and cmd/fleet/mail.go. Same pattern as the prior extractions on this issue (#44929, #45343, #45583, #46166, #46421, #46517, #46742, #46830, #46893). Both are best-effort startup providers — they log and fall back rather than aborting boot — so they group naturally.

Functions:

  • initGeoIP — returns the GeoIP provider. When no database path is configured, or the MaxMind database fails to load, it returns a no-op provider and logs rather than aborting startup.
  • initMailService — configures the mail service; a construction failure is logged and the (possibly nil) service is returned, matching the prior best-effort behavior.
  • shouldForceSMTPBackend — the SMTP-vs-custom-backend rule, pulled out so the decision is its own testable unit: SMTP and a custom email backend are mutually exclusive, and an already-enabled SMTP configuration wins.

Behavior is preserved — runServeCmd calls these in the same place with the same arguments, and the full cmd/fleet suite passes against MySQL + Redis. The mail block's config.Email.EmailBackend reset is local to mail construction (nothing downstream reads it), so moving it into initMailService is behavior-identical.

On test scope: TestInitGeoIP pins the not-fatal fallback for both the missing-path and invalid-path cases — GeoIP being best-effort is a real guarantee worth locking. TestShouldForceSMTPBackend covers the backend mutual-exclusion decision, including the nil app config / nil SMTP settings edges. I didn't add a full initMailService happy-path unit test: mail.NewService builds real SMTP/SES backends, so that path is exercised by booting the server.

Related issue: Refs #33370

Checklist for submitter

  • Added/updated automated tests
  • Changes file: not applicable — internal refactor with no user-visible behavior change

Summary by CodeRabbit

Release Notes

  • Refactor

    • Improved GeoIP initialization with automatic fallback when database configuration is unavailable
    • Enhanced mail service initialization with better error handling during startup
    • Refined SMTP backend precedence logic
  • Tests

    • Added comprehensive unit tests for GeoIP and mail service initialization scenarios

Move the best-effort provider setup into cmd/fleet/geoip.go and
cmd/fleet/mail.go. initGeoIP falls back to a no-op provider when the
database path is missing or invalid rather than aborting boot;
shouldForceSMTPBackend encodes the SMTP-vs-custom-backend mutual
exclusion so the decision is unit-testable.

Refs fleetdm#33370
@raju249
raju249 requested a review from a team as a code owner June 9, 2026 06:28
@coderabbitai

coderabbitai Bot commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: fdb81122-c8b4-422f-b1ce-6e65ce0ef771

📥 Commits

Reviewing files that changed from the base of the PR and between 446b46e and 6170c21.

📒 Files selected for processing (5)
  • cmd/fleet/geoip.go
  • cmd/fleet/geoip_test.go
  • cmd/fleet/mail.go
  • cmd/fleet/mail_test.go
  • cmd/fleet/serve.go

Walkthrough

This pull request extracts initialization logic for GeoIP and mail services from cmd/fleet/serve.go into dedicated helper modules. A new cmd/fleet/geoip.go module provides initGeoIP for best-effort GeoIP provider initialization with fallback to no-op. A new cmd/fleet/mail.go module provides shouldForceSMTPBackend to detect backend conflicts and initMailService to construct the mail service with error logging. Each new module includes unit tests. The serve.go file is simplified by removing the direct mail import and delegating to these helpers instead of inline initialization.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main change: extracting geoIP and mail service initialization out of runServeCmd into separate modules.
Description check ✅ Passed The description covers objectives, functions, behavior preservation, and testing approach comprehensively; it references related issue #33370 and explains why certain tests were or weren't added.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@raju249

raju249 commented Jun 9, 2026

Copy link
Copy Markdown
Contributor Author

Thanks for the continued support @MagnusHJensen on the code reviews.

This is another jab at the refactoring, this time, I separated small blocks in files of their own along with the test. Mind taking a look, please?

Thanks so much!

@codecov

codecov Bot commented Jun 9, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 54.16667% with 11 lines in your changes missing coverage. Please review.
✅ Project coverage is 67.19%. Comparing base (446b46e) to head (6170c21).

Files with missing lines Patch % Lines
cmd/fleet/mail.go 38.46% 8 Missing ⚠️
cmd/fleet/serve.go 0.00% 2 Missing ⚠️
cmd/fleet/geoip.go 88.88% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main   #47151      +/-   ##
==========================================
- Coverage   67.19%   67.19%   -0.01%     
==========================================
  Files        2915     2917       +2     
  Lines      226368   226376       +8     
  Branches    11720    11720              
==========================================
+ Hits       152106   152108       +2     
- Misses      60522    60523       +1     
- Partials    13740    13745       +5     
Flag Coverage Δ
backend 68.78% <54.16%> (-0.01%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@MagnusHJensen MagnusHJensen 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.

Thanks again @raju249

@MagnusHJensen
MagnusHJensen merged commit 2def0f2 into fleetdm:main Jun 9, 2026
36 checks passed
@raju249
raju249 deleted the 33370-geoip-mail-extraction branch June 9, 2026 08:24
MagnusHJensen pushed a commit that referenced this pull request Jun 17, 2026
Extracts the cron schedule registration out of `runServeCmd` and into a
new `cmd/fleet/cron_registration.go`. Same pattern as the prior
extractions on this issue (#44929, #45343, #45583, #46166, #46421,
#46517, #46742, #46830, #46893, #47151). This is the largest slice so
far — `runServeCmd` drops from ~1300 to ~1000 lines, and `serve.go` from
1776 to 1472.

The 33 `StartCronSchedule` registrations move into one
`startCronSchedules` entry point backed by a `cronSchedulesDeps` struct
(the dependencies the closures previously captured from `runServeCmd`).
Registration is grouped by domain:

- `registerCleanupAndMaintenanceCrons` — chart data collection, the
`cron_stats` cleanup goroutine, software migrations, frequent cleanups,
cleanups-then-aggregation, query results cleanup, upcoming activities,
usage statistics, batch activities.
- `registerVulnerabilityCrons` — the vulnerabilities schedule, or the
remote-trigger proxy when processing is disabled on this instance.
- `registerWorkerCrons` — automations and worker integrations.
- `registerMDMCrons` — Apple MDM worker, DEP profile assigner, service
discovery, the Apple/Windows/Android profile managers, the Android
device reconciler, the Android policy migrations, and the APNs pusher.
- `registerPremiumCrons` — iPhone/iPad refetcher and reviver, maintained
apps, VPP app version refresh (and the one-shot VPP country backfill),
recovery lock passwords, managed local account rotation, activities
streaming, and the calendar schedule.
- `registerMiscCrons` — host vitals label membership and the batch
activity completion checker.

Behavior is preserved — the schedules register in the same order with
the same arguments, the same conditionals gate them (premium, audit log,
env vars, software store presence), and the `config` is threaded as a
pointer so the `&config` and `config.Calendar` mutations inside the
calendar closure keep their original semantics. `cmd/fleet/cron.go` (the
schedule definitions) is intentionally untouched; only the wiring moved.

One unit test added: `TestVulnerabilityProcessingDisabled` covers the
vuln enable/disable predicate extracted into
`vulnerabilityProcessingDisabled`, including the legacy
`current_instance_checks` `"0"` value. The rest of the file is
dependency-wiring relocation with no further decision logic to unit-test
— those paths construct real schedules, so they stay covered by the
existing suite and integration tests. The full `cmd/fleet` suite passes
against MySQL + Redis, and a local server boot confirms the same 30 cron
schedules start as before (verified against the "started cron schedules"
log line).

**Related issue:** Refs #33370

# Checklist for submitter

- [x] Added/updated automated tests
- [x] QA'd all new/changed functionality manually (verified via local
server boot — same 30 cron schedules start)
- Changes file: not applicable — internal refactor with no user-visible
behavior change


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Refactor**
* Centralized background cron schedule startup and standardized job
initialization sequencing for maintenance, vulnerability handling,
integrations, MDM workflows, and premium tasks.
* **New Features / Behavior**
* Added config- and license-controlled enablement for vulnerability
processing (local vs remote triggering), MDM automation (including APNs
delivery and device reconciliation), and premium-only refresh/recovery
behaviors.
* Made chart data collection and optional activity streaming
configurable, with safe fallbacks for scheduling periodicity.
* **Tests**
* Added coverage for vulnerability-schedule enable/disable decision
logic.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
JordanMontgomery pushed a commit that referenced this pull request Jun 24, 2026
Extracts the `/api/` request timeout/body-size override middleware out
of `runServeCmd` and into `apiTimeoutOverrideHandler` in a new
`cmd/fleet/http_middleware.go`. Same pattern as the prior extractions on
this issue (#44929, #45343, #45583, #46166, #46421, #46517, #46742,
#46830, #46893, #47151, #47562). `runServeCmd` drops from ~1000 to ~900
lines, and `serve.go` from 1475 to 1373.

The middleware is the `~100`-line `rootMux.HandleFunc("/api/", ...)`
closure that applies per-route read/write deadline overrides for
endpoints that legitimately run long — synchronous script runs, large
software-installer and bootstrap-package uploads, the Android enterprise
signup SSE stream, and large MDM profile batch operations — and, for
package-upload routes, caps the request body and threads the configured
max installer size through the request context.

Behavior is preserved — the handler is moved verbatim and wired into
`rootMux` via a single `apiTimeoutOverrideHandler(apiHandler, config,
logger)` call, so the same routes get the same overrides and every
request still falls through to `apiHandler.ServeHTTP`. The now-unused
`scripts` and `installersize` imports drop out of `serve.go`.

On test scope: `TestAPITimeoutOverrideHandler` verifies the real
decision in this middleware — that package-upload paths thread the
configured max installer size into the request context (and non-upload
requests keep the default) — and that the wrapped API handler is always
invoked. The deadline overrides themselves go through
`http.ResponseController`, which a unit-test `ResponseRecorder` doesn't
support (the handler logs and proceeds, as in production), so those are
exercised by booting the server rather than asserted in a unit test.

**Related issue:** Refs #33370

# Checklist for submitter

- [x] Added/updated automated tests
- [x] QA'd all new/changed functionality manually (verified via local
server boot)
- Changes file: not applicable — internal refactor with no user-visible
behavior change

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Refactor**
* Improved timeout handling for long-running operations across the API.
Script execution, file uploads, Server-Sent Event streams, and batch
operations now have optimized request timeouts and body size limits.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
juan-fdz-hawa pushed a commit that referenced this pull request Jun 30, 2026
)

Adds an end-to-end boot test for `runServeCmd`, the main server entry
point. This is the coverage milestone for #33370: `serve.go` goes from
~7% to ~64%, and `runServeCmd` itself from 0% to ~62%.

The earlier PRs on this issue (#44929, #45343, #45583, #46166, #46421,
#46517, #46742, #46830, #46893, #47151, #47562, #47891) extracted
testable pieces out of `runServeCmd`, but the function itself stayed at
0% — it blocks on an OS signal and wires the entire server together, so
the only way to cover it is to actually boot it. This PR does that.

`TestRunServeCmd` (gated behind `MYSQL_TEST` + `REDIS_TEST`) boots the
full server against a real migrated test MySQL and Redis, waits for
`/healthz`, then cancels the command context to trigger a graceful
shutdown. It covers two paths:

- **Full boot with Apple MDM enabled** — a 32-byte server private key
brings up the Apple MDM protocol services and the host-identity /
conditional-access SCEP setup, so the boot exercises the MDM startup
path as well as the core wiring, cron schedules, and HTTP server.
- **Fail-fast on bad config** — an invalid Redis host-cache
configuration (enabled with a non-positive TTL) aborts startup through
`initFatal` and returns rather than serving, covering the Redis-init
error path and the nil-pool guard.

Beyond coverage, this doubles as a regression net for the ongoing
`runServeCmd` slicing: a future change that breaks startup now fails
this test instead of reaching a release.

**One production change**, in `runServeCmd`'s shutdown `select`: it now
also watches `cmd.Context().Done()`. This is inert in production — the
root command runs via `Execute()` (not `ExecuteContext()`), so
`cmd.Context()` is `context.Background()` and never cancels. Only the
test runs the command with a cancelable context, which is how it shuts
the server down without sending a real signal (a `SIGTERM` would kill
the test binary).

A couple of notes for reviewers:
- The test uses `os.Setenv` (not `t.Setenv`) because the MySQL test
helper marks the test parallel; the boot scenarios run as serial
subtests so the process-global config env doesn't race.
- `runServeCmd` registers metrics with the process-global Prometheus
registry, which can only happen once per process, so there is a single
full boot here; the error-path scenario fails before that registration.
- The test DB is loaded from a schema dump that doesn't mark every data
migration as applied, so the boot runs with
`FLEET_UPGRADES_ALLOW_MISSING_MIGRATIONS=1`.

It adds ~2s to the `cmd/fleet` (`main`) test bundle, which is well off
the CI critical path.

**Related issue:** Refs #33370

# Checklist for submitter

- [x] Added/updated automated tests
- [x] QA'd all new/changed functionality manually (verified locally:
boots to /healthz, graceful shutdown, ~64% serve.go coverage)
- Changes file: not applicable — internal test coverage with no
user-visible behavior change

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Bug Fixes**
* Improved server shutdown handling to stop cleanly when the running
command’s context is canceled, not only on OS signals.
* Added stronger startup validation to fail fast for invalid Redis
host-cache configuration (e.g., non-positive TTL).
* **Tests**
* Added an end-to-end test that boots the server against real
MySQL/Redis, verifies graceful startup/shutdown, and confirms fast-fail
behavior for misconfiguration.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
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.

2 participants