Containerize Notification service (hardened image + health/ready probes)#83
Containerize Notification service (hardened image + health/ready probes)#83devin-ai-integration[bot] wants to merge 1 commit into
Conversation
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
There was a problem hiding this comment.
🔍 Default connection string removed only for Notification service, inconsistent with other services
The ConnectionStrings.DefaultConnection was removed from appsettings.json, but all other services (Order, Customer, Identity, Product) still have their connection strings in their respective appsettings.json files (e.g. src/Services/Order/Order.API/appsettings.json:9). In Docker, this works because docker-compose.yml provides the connection string via environment variable. For local development without Docker, GetConnectionString("DefaultConnection") returns null, which is passed to UseNpgsql(). The EnsureCreated() failure is caught by the new try/catch, and the /ready health check will report 503 — so the app starts but is non-functional for DB operations. This may be intentional (avoiding credentials in config), but the inconsistency with other services and lack of an appsettings.Development.json fallback is worth confirming.
Was this helpful? React with 👍 or 👎 to provide feedback.
There was a problem hiding this comment.
Intentional — the goal here is externalizing config so no credentials are baked into the image; the connection string comes from ConnectionStrings__DefaultConnection, which compose supplies. The startup EnsureCreated() is wrapped in try/catch so liveness (/health) never depends on the DB, and /ready correctly reports 503 when the DB is unreachable (e.g. local run without Docker/env var). The inconsistency with the other services' appsettings.json is expected: this PR is scoped to Notification, and those services will be aligned separately.
| <ProjectReference Include="..\..\..\Shared\Shared.Contracts\Shared.Contracts.csproj" /> | ||
| <ProjectReference Include="..\..\..\Shared\Shared.Infrastructure\Shared.Infrastructure.csproj" /> |
There was a problem hiding this comment.
🔍 Shared project reference path fix is correct but other services still have the broken path
The change from ..\..\Shared\... to ..\..\..\Shared\... is a genuine fix. From src/Services/Notification/Notification.API/, the old path ../../Shared resolves to src/Services/Shared/ which does not exist — the actual location is src/Shared/. The new three-level-up path correctly resolves to src/Shared/. However, all other services (Order, Customer, Product, Identity) still use the two-level ..\..\Shared\... path in their .csproj files (e.g. src/Services/Order/Order.API/Order.API.csproj:10-11). Those references are equally broken. It's possible the solution file or a Directory.Build.props was masking this, but the paths are objectively wrong for both local and Docker builds.
Was this helpful? React with 👍 or 👎 to provide feedback.
There was a problem hiding this comment.
Correct, and intentional. This PR is deliberately scoped to containerizing Notification only, so I fixed only Notification.API.csproj — that fix is required because the Notification image can't build otherwise. The other services (Order, Customer, Product, Identity) have the same broken ..\..\Shared\... path but were already containerized in prior PRs and building, so I left them untouched to keep this PR scoped. Fixing them belongs in a separate change.
Summary
Containerizes the Notification service: builds into a hardened image, exposes liveness/readiness probes, takes all config from the environment, and fixes the known build-breaking project references so the image can actually build. Scoped to Notification only.
Build-breaking fix (required for the image to build).
Notification.API.csprojreferenced Shared projects one directory too shallow, resolving to a non-existentServices/Shared:(from
src/Services/Notification/Notification.APIyou need 3..to reachsrc/Shared). No other services' references were touched.Multi-stage
Dockerfile(sdk:10.0build →aspnet:10.0runtime): per-.csprojCOPY for layer-cached restore,curlinstalled in the runtime stage (not present in the aspnet image, needed by HEALTHCHECK),USER $APP_UID(non-root), hardcoded internal port5005(ASPNETCORE_URLS=http://+:5005; remap via-p), andHEALTHCHECKprobing/health(liveness, not readiness — so transient DB outages don't restart the container)./health+/ready(+/healthzalias) inProgram.cs:Added the
Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCorepackage forAddDbContextCheck.Startup DB init made non-fatal. The existing
db.Database.EnsureCreated()at startup is now wrapped in try/catch so liveness does not depend on the DB being reachable at boot (readiness reflects DB state instead). Without this the process crashes at startup when no DB is present, so/healthcould never return 200 without a DB.Externalized config. Removed the hardcoded
ConnectionStrings:DefaultConnection(Host=localhost;...;Password=postgres) fromappsettings.json; it now comes only fromConnectionStrings__DefaultConnection, which compose already supplies..dockerignoreadded at the build-context root (src/) excluding build artifacts and local secrets.Compose: the
notification-serviceblock already existed (port 5005,depends_onpostgres+rabbitmq, connection string via env) — verified, no edit needed.docker compose configvalidates.Verification (real Docker, before this PR)
dotnet build ... -c Release— succeeds, 0 warnings/errors.src/.GET /health→ 200 Healthy,GET /healthz→ 200,GET /ready→ 503 Unhealthy.GET /ready→ 200 Healthy (notificationdbauto-created on startup).docker inspecthealth status → healthy;docker exec ... id→ uid=1654(app) (non-root).Link to Devin session: https://partner-workshops.devinenterprise.com/sessions/a5e67a775bea4d8a9a34de7f7bd3fdbd
Requested by: @mbatchelor81