Add unit tests for KubernetesCluster, Tenant, ServiceInstance, and RegisterClusterHandler
Some checks failed
Build EntKube / build (push) Failing after 27s
Package Helm Chart / lint (push) Failing after 36s
Package Helm Chart / package (push) Has been skipped

- Implement tests for KubernetesCluster including registration, connectivity status, and error handling.
- Create tests for Tenant creation, member management, and status changes.
- Add tests for ServiceInstance provisioning and state management.
- Introduce RegisterClusterHandler tests to validate registration requests and error scenarios.
- Set up project files for new test projects with necessary dependencies.
This commit is contained in:
Nils Blomgren
2026-05-05 11:44:36 +02:00
parent 461fa36a46
commit a96dd33039
203 changed files with 2876 additions and 553 deletions

View File

@@ -400,60 +400,91 @@ If the service or project you are working on does not have a corresponding test
## Architecture
### Modular Monolith with Blazor
### Microservices with Blazor BFF
EntKube is a **modular monolith** — a single deployable Blazor application with clearly separated domain modules internally. This keeps deployment simple while maintaining clean boundaries between concerns.
EntKube follows a **microservices architecture** with a Blazor BFF (Backend-for-Frontend) as the user-facing entry point. Each service owns its bounded context, has its own data store, and can be deployed and scaled independently. This is NOT a nano-services architecture — we split by meaningful business boundaries, not by technical layers.
#### Core Principles
- **Domain modules within one application**: Each business capability (cluster management, service provisioning, tenant management, etc.) lives in its own namespace/folder but deploys as part of the single application
- **Clear module boundaries**: Modules communicate through well-defined interfaces — never reach directly into another module's internals
- **Shared database with schema separation**: The single application owns its database, but each module owns its tables/schema area
- **Extract to a service only when necessary**: If a module genuinely needs independent scaling or a separate lifecycle, extract it then — not before
- **4 services, each with a clear responsibility**: Web (BFF), Clusters, Provisioning, Identity
- **Each service owns its data**: No shared databases between services
- **Services communicate via HTTP APIs**: Simple REST calls between services, with resilient retry policies
- **SharedKernel for contracts only**: Shared types (Result, ApiResponse, base Entity) live in a shared library — but no shared business logic
- **Feature folders over layer folders**: Each feature is a vertical slice (handler + endpoint + related types in one folder)
#### When to Extract a Module to a Separate Service
- The module has drastically different scaling requirements
- The module needs to be deployed on a different cadence
- The module introduces an external integration that benefits from fault isolation
#### Service Boundaries
| Service | Responsibility | Port (dev) |
|---------|---------------|-------------|
| **EntKube.Web** | Blazor BFF — serves UI, proxies API calls to backend services, owns user auth session | 5000 |
| **EntKube.Clusters** | Kubernetes cluster registration, health monitoring, API connectivity | 5010 |
| **EntKube.Provisioning** | Shared service lifecycle (MinIO, CNPG, Keycloak) — provisioning, reconciliation, teardown | 5020 |
| **EntKube.Identity** | Tenant management, user membership, roles, Keycloak integration | 5030 |
#### Anti-Patterns to Avoid
```
# ❌ BAD: Premature microservices for a platform that deploys as one unit
Services/
├── ClusterService/
├── TenantService/
├── MonitoringService/
└── ProvisioningService/ # All deployed together anyway
# ❌ BAD: Nano-services — splitting too granularly
MinIOService/
CloudNativePGService/
KeycloakService/
HealthCheckService/ # These belong together under "Provisioning"
# ✅ GOOD: Modules within the monolith with clear boundaries
EntKube/
├── Clusters/ # Cluster management module
├── Tenants/ # Multi-tenant module
├── Monitoring/ # Observability module
├── Provisioning/ # Service provisioning module
── Shared/ # Cross-cutting concerns
# ❌ BAD: Shared database between services
# Services must own their own data — cross-service queries go through APIs
# ✅ GOOD: Meaningful service boundaries with feature folders
src/
├── EntKube.Web/ # Blazor BFF
── EntKube.Clusters/ # Cluster management service
│ ├── Domain/ # Aggregates, value objects, repository contracts
│ ├── Features/ # Vertical slices (RegisterCluster/, GetClusters/, etc.)
│ └── Infrastructure/ # Repository implementations, external integrations
├── EntKube.Provisioning/ # Service provisioning service
│ ├── Domain/
│ ├── Features/
│ └── Infrastructure/
├── EntKube.Identity/ # Identity & tenant service
│ ├── Domain/
│ ├── Features/
│ └── Infrastructure/
└── EntKube.SharedKernel/ # Shared contracts (Result, ApiResponse, base Entity)
```
### Project Structure
```
Solution/
├── EntKube/ # Blazor Server host (BFF)
│ ├── Components/ # Razor components, layouts, pages
│ ├── Data/ # EF Core DbContext and migrations
├── Clusters/ # Kubernetes cluster management
│ ├── Provisioning/ # Shared service provisioning (MinIO, CNPG, Keycloak)
├── Tenants/ # Multi-tenant configuration
├── Monitoring/ # Health, metrics, observability
└── Pipelines/ # CI/CD pipeline integration
├── EntKube.Client/ # Blazor WebAssembly client
── Pages/ # Interactive WASM pages
── wwwroot/ # Client static assets
├── Charts/ # Helm charts for deployment
├── src/
│ ├── EntKube.SharedKernel/ # Shared types and contracts between services
├── Domain/ # Result, Entity base class
│ └── Contracts/ # ApiResponse envelope, DTOs
│ ├── EntKube.Web/ # Blazor Server BFF
│ ├── Components/ # Razor components, layouts, pages
│ ├── Data/ # EF Core DbContext (Identity only)
│ └── wwwroot/ # Static assets
├── EntKube.Web.Client/ # Blazor WebAssembly client
│ └── Pages/ # Interactive WASM pages
── EntKube.Clusters/ # Cluster management API
│ │ ├── Domain/ # KubernetesCluster aggregate
│ │ ├── Features/ # RegisterCluster/, GetClusters/
│ │ └── Infrastructure/ # Repository implementations
│ ├── EntKube.Provisioning/ # Service provisioning API
│ │ ├── Domain/ # ServiceInstance aggregate
│ │ ├── Features/ # ProvisionService/, GetServices/
│ │ └── Infrastructure/ # Repository implementations
│ └── EntKube.Identity/ # Identity & tenant API
│ ├── Domain/ # Tenant aggregate
│ ├── Features/ # CreateTenant/
│ └── Infrastructure/ # Repository implementations
├── tests/
│ ├── EntKube.Clusters.Tests/ # Unit + integration tests
│ ├── EntKube.Provisioning.Tests/
│ ├── EntKube.Identity.Tests/
│ └── EntKube.Web.Tests/
├── Charts/ # Helm charts for deployment
│ └── entkube/
│ ├── Chart.yaml
│ ├── values.yaml
│ └── templates/
└── Tests/
└── *.Tests/ # xUnit test projects
└── .gitea/workflows/ # Gitea Actions CI/CD
```
### Deployment & Infrastructure
@@ -511,10 +542,11 @@ jobs:
```
### Communication Patterns
- Internal module communication via dependency injection and in-process calls
- Kubernetes API communication for cluster management operations
- HTTP APIs exposed for external integrations
- Use resilient connections with retry policies (Polly) for external calls
- **Service-to-service**: HTTP REST via typed HttpClient with Polly retry policies
- **BFF-to-service**: The Web BFF proxies user requests to the appropriate backend service
- **Kubernetes API**: The Clusters service communicates with k8s API servers using the official .NET client
- **Async workflows**: Background services within each microservice handle reconciliation loops (e.g., provisioning, health checks)
- **No message bus yet**: Start with synchronous HTTP; extract to async messaging (NATS, RabbitMQ) only when proven necessary
### Database