Skip to content

Commit 5ef163d

Browse files
authored
Merge pull request #174 from vshn/adr-framework-2
Add ADR AppCat Framework 2.0
2 parents 757b080 + d46311c commit 5ef163d

19 files changed

+1121
-7
lines changed
127 KB
Loading
38.2 KB
Loading

docs/modules/ROOT/pages/adr/0049-migrate-minio-to-rclone.adoc renamed to docs/modules/ROOT/pages/adr/0041-migrate-minio-to-rclone.adoc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
= ADR 0049 - Migrate MinIO to Rclone
1+
= ADR 0041 - Migrate MinIO to Rclone
22
:adr_author: Mike Ditton
33
:adr_owner: Schedar
44
:adr_reviewers: Schedar

docs/modules/ROOT/pages/adr/0050-wazuh-agent-for-appcat-services.adoc renamed to docs/modules/ROOT/pages/adr/0043-wazuh-agent-for-appcat-services.adoc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
= ADR 0050 - Wazuh Agent for AppCat Services
1+
= ADR 0043 - Wazuh Agent for AppCat Services
22
:adr_author: Simon Beck
33
:adr_owner: Schedar
44
:adr_reviewers:
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
= ADR 0044 - Configuration Language: KCL vs CUE
2+
:adr_author: Gabriel Saratura
3+
:adr_owner: Schedar
4+
:adr_reviewers: Schedar
5+
:adr_date:
6+
:adr_upd_date:
7+
:adr_status: draft
8+
:adr_tags: framework,framework2,kcl,cue,configuration
9+
10+
include::partial$adr-meta.adoc[]
11+
12+
[NOTE]
13+
.Summary
14+
====
15+
We choose https://www.kcl-lang.io/[KCL] over https://cuelang.org/[CUE] for type-safe, constraint-based configuration tool.
16+
KCL satisfies our needs for service definitions, helm release mapping, and platform schemas in Framework 2.0.
17+
====
18+
19+
== Problem
20+
21+
Need a type-safe, constraint-based configuration language for defining service plans, Helm value mappings, and cluster configurations.
22+
Must support validation, composition, and reusability while being maintainable by Service Maintainers and Framework Engineers, and align with how each persona works:
23+
24+
* *Service Maintainer*: author services and define parameter-to-Helm mappings without Go, with clear validation errors and minimal boilerplate.
25+
* *Framework Engineer*: enforce platform base schemas, facilitates generation of service bundles, handles platform resources via appcat-catalogs in GitOps style.
26+
* *Service Operator*: rely on consistent API surfaces for troubleshooting and upgrades; need deterministic mappings and readable errors when validation fails.
27+
* *Service User*: see only safe, validated parameters in the portal; prevent invalid combinations from ever reaching the runtime.
28+
29+
=== Solutions
30+
31+
==== Option A: KCL (Kubernetes Configuration Language)
32+
33+
**Description:**
34+
35+
https://www.kcl-lang.io/[KCL] is a constraint-based configuration language designed for Kubernetes.
36+
Features include strong typing with compile-time validation, lambda functions for transformations, and native Kubernetes resource understanding.
37+
38+
**Advantages:**
39+
40+
* *Strong Typing*: Compile-time validation of schemas and data structures catches configuration errors before deployment
41+
* *Constraint-Based*: Define validation rules and constraints (for example, CPU must match regex pattern, memory within allowed ranges)
42+
* *Built for Kubernetes*: Native understanding of Kubernetes resource structures reduces boilerplate
43+
* *Lambda Functions*: Natural expression of parameters/plan-to-Helm-value mapping functions, critical for service configuration
44+
* *Good Error Messages*: Clear validation errors with line numbers guide Service Maintainers to issues
45+
* *Managed Resources Support*: Import system enables base configurations with distribution-specific overrides
46+
* *Override Pattern*: Kapitan style of resolving hierarchical configuration structure in the platform repository
47+
* *Active Development*: Backed by Ant Group with growing community and ecosystem
48+
49+
**Disadvantages:**
50+
51+
* *Smaller Ecosystem*: Fewer tools and IDE support compared to more established languages like CUE
52+
* *Learning Curve*: Service Maintainers/Framework Engineers need to learn new syntax and constraint-based thinking
53+
* *Less Mature*: Newer language with potential for breaking changes as it evolves
54+
* *Limited Resources*: Fewer tutorials and examples
55+
56+
==== Option B: CUE (Configure Unify Execute)
57+
58+
**Description:**
59+
60+
https://cuelang.org/[CUE] is a data constraint language where types and values are unified.
61+
Configurations compose through constraint unification.
62+
Excels at validation but lacks native lambda functions - transformation logic must be expressed through constraint systems, making some operations such as Helm value mappings verbose.
63+
64+
**Advantages:**
65+
66+
* *Unification Model*: Types and values unified - constraints automatically propagate and validate across compositions
67+
* *Exceptional Data Validation*: Catch configuration errors through mathematical constraint solving before any runtime execution
68+
* *Mature Tooling*: Language Server Protocol, IDE support, comprehensive CLI tools
69+
* *Constraint Propagation*: Automatically validates that all configuration layers satisfy constraints without explicit checks
70+
* *Export Flexibility*: Can export to multiple formats (JSON, YAML, Protocol Buffers) natively
71+
72+
**Disadvantages:**
73+
74+
* *Steep Learning Curve*: Unification model requires different thinking - declarative constraints vs procedural logic
75+
* *Less Imperative*: Difficult to express procedural transformations like Helm value mapping with conditional logic
76+
* *No Native Lambda Functions*: Must encode logic as constraints
77+
* *Verbose Transformations*: Complex mapping functions require extensive boilerplate, reducing Service Maintainer velocity
78+
* *Limited Procedural Logic*: "If-then-else" patterns become constraint systems that are harder to read and maintain
79+
80+
=== Decision
81+
82+
*We use KCL.*
83+
84+
==== Rationale
85+
86+
1. *Lambda Functions*: Service Maintainers need to define their services with constraints defined by Framework Engineers. KCL's lambda support makes this natural and concise.
87+
88+
2. *Kubernetes-Native*: KCL is designed specifically for Kubernetes configuration, with built-in understanding of K8s resource structures.
89+
90+
3. *Procedural Logic*: Custom Service configuration often requires conditional logic. KCL handles this more naturally than CUE.
91+
92+
4. *Simpler Mental Model*: For Service Maintainers and Framework Engineers, KCL's imperative style is closer to traditional programming, reducing learning curve.
93+
94+
5. *Crossplane Integration*: KCL Crossplane https://artifacthub.io/packages/kcl/kcl-module/crossplane[modules] available for Service Maintainers.
95+
96+
6. *Override and Packaging Fit*: KCL works with profile overrides (Kapitan substitution).
97+
98+
7. *Error Quality for Maintainers*: KCL's validation errors help Service Maintainers debug services quickly, reducing turnaround time versus CUE’s more abstract unification errors.
99+
100+
8. *Future Service Evolution*: As Framework 2.0 targets a single generic composition function consuming unstructured inputs, keeping service logic in KCL avoids per-service Go changes while still providing expressive mapping hooks.
101+
102+
**Trade-off Accepted:** Smaller ecosystem and tooling compared to CUE, but better fit for our use case.
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
= ADR 0045 - Service Orchestration: Crossplane 2.x
2+
:adr_author: Gabriel Saratura
3+
:adr_owner: Schedar
4+
:adr_reviewers: Schedar
5+
:adr_date:
6+
:adr_upd_date:
7+
:adr_status: draft
8+
:adr_tags: framework,framework2,crossplane,composition,orchestration
9+
10+
include::partial$adr-meta.adoc[]
11+
12+
[NOTE]
13+
.Summary
14+
====
15+
This ADR defines the service orchestration mechanism for AppCat Framework 2.0, evaluating Crossplane 2.x composition functions against a custom Kubernetes operator.
16+
17+
The decision selects https://www.crossplane.io/[Crossplane] 2.0 with composition functions as the orchestration layer, providing generic service management with built-in revision control and clear separation between framework code (https://go.dev/[Go]) and service configuration (https://www.kcl-lang.io/[KCL]).
18+
====
19+
20+
== Context
21+
22+
=== Problem Statement
23+
24+
The AppCat Framework 2.0 requires a service orchestration mechanism that:
25+
26+
* Renders Helm charts based on service configuration and user-selected parameters
27+
* Manages complete service lifecycle (create, update, delete)
28+
* Handles connection secrets and billing/maintenance/backup/metrics integration
29+
* Allows Framework Engineers to maintain core logic in Go
30+
* Enables Service Maintainers to add new services without touching Go code
31+
* Provides versioning and revision management for controlled rollouts
32+
33+
=== Architectural Context
34+
35+
The framework architecture separates concerns across three layers:
36+
37+
1. **Framework Engineers** maintain the orchestration runtime (Go composition functions)
38+
2. **Service Maintainers** define service configurations (KCL compiled managed resource objects)
39+
3. **Service Users** consume services via declarative API (XRD/Composite resources)
40+
41+
The orchestration layer sits at the boundary between framework runtime and service configuration, responsible for:
42+
43+
* Loading service configurations at runtime
44+
* Merging static defaults with dynamic user parameters
45+
* Generating Kubernetes resources (HelmRelease, K8up backup schedules, ServiceMonitor, secrets, etc.)
46+
* Tracking revisions for upgrade workflows
47+
* Propagating status and errors to users
48+
49+
== Options
50+
51+
=== Option A: Crossplane 2.x with Composition Functions
52+
53+
==== Description
54+
55+
https://www.crossplane.io/[Crossplane] 2.0 composition functions execute in pipeline mode during reconciliation, receiving composite state and returning desired resources.
56+
A single generic function handles all services by loading service-specific configuration Kubernetes like object (ex:, `RedisServiceConfig`) at runtime, merging user parameters, and generating resources.
57+
Other functions can be added to increase framework quality such as increased service resilience and error handling.
58+
59+
==== Advantages
60+
61+
* **Generic Function Pattern**: Single function handles all services without Go code changes
62+
* **Built-in Composition Revisions**: Native versioning and gradual rollout capabilities
63+
* **Clear Separation**: Framework code (Go) completely separated from service configuration (KCL)
64+
* **Provider Ecosystem**: Leverage existing Crossplane providers (Helm, SQL) instead of custom implementations
65+
* **Status Aggregation**: Automatic status propagation from managed resources
66+
* **Existing Team Knowledge**: Team already proficient with Crossplane patterns
67+
68+
==== Disadvantages
69+
70+
* **Learning Curve**: Crossplane concepts (XRD, Composition, Revisions) require initial learning investment, though team already has experience
71+
* **Abstraction Overhead**: Multiple layers (Composite → Composition Function → HelmRelease → Helm Chart) vs direct operator reconcile loop
72+
* **Debugging Complexity**: Tracing issues through pipeline requires understanding Crossplane request/response flow and function logs
73+
* **Function Packaging**: Each function version requires OCI image build and registry push
74+
* **Performance**: Function invoked via gRPC adds latency compared to in-process operator reconciliation
75+
76+
=== Option B: Custom Kubernetes Operator
77+
78+
==== Description
79+
80+
Build a custom Kubernetes operator using https://github.com/operator-framework/operator-sdk[operator-sdk] that directly reconciles service CRDs and renders Helm charts.
81+
The operator would either use per-service controllers or a generic controller loading service configuration at runtime, with custom implementations for revision management and upgrade workflows.
82+
83+
==== Advantages
84+
85+
* **Simpler Mental Model**: Direct reconciliation loop easier to understand than pipeline abstraction
86+
* **Faster Debugging**: Single operator process, direct logging, simpler to trace issues with standard Go debugging
87+
* **More Control**: Full control over reconciliation logic, status updates, error handling, retry behavior
88+
* **No Crossplane Dependency**: One less external dependency to manage and upgrade
89+
* **Familiar Pattern**: Standard operator pattern widely used across Kubernetes ecosystem
90+
* **Performance**: In-process reconciliation potentially faster than gRPC function invocation
91+
92+
==== Disadvantages
93+
94+
* **Framework Coupling**: Risk of service-specific logic creeping into operator code over time
95+
* **No Built-in Revisions**: Must implement version management, gradual rollouts, and revision tracking ourselves (complex)
96+
* **Reinventing Wheels**: Helm integration, status aggregation, provider patterns already exist in Crossplane
97+
* **More Code to Maintain**: Custom implementations needed for operator, CRD management, status propagation, connection secrets all custom implementations
98+
* **Migration Cost**: Existing Crossplane investment (team knowledge, component-appcat patterns) cannot be leveraged
99+
* **Revision Management**: Composition revisions essential for maintenance workflows would require significant custom development
100+
101+
=== Options Not Investigated
102+
103+
The following orchestration approaches were considered but excluded early as they do not meet core requirements:
104+
105+
* **https://fluxcd.io/[Helm Operator (Flux)]**: Flux's Helm controller reconciles HelmRelease resources but lacks service configuration logic, connection secret generation, billing integration - essentially requires rebuilding Crossplane capabilities.
106+
107+
* **https://developer.hashicorp.com/terraform[Terraform Operator]**: Terraform excels at cloud infrastructure provisioning but adds unnecessary complexity for Kubernetes-native workloads.
108+
Requires maintaining Terraform modules alongside Helm charts, mixing declarative models, and does not provide native Kubernetes resource management.
109+
110+
* **https://kubevela.io/[KubeVela]**: Application-centric platform with promising capabilities but immature ecosystem compared to Crossplane.
111+
Application model doesn't align with "Helm Chart in" vision, and has less adoption in managed service space.
112+
Provides similar capabilities to Crossplane but with smaller community and fewer production deployments.
113+
114+
These options lack critical features (revision management, status aggregation, provider ecosystem) or introduce unnecessary abstraction layers without clear benefits over Crossplane 2.x composition functions.
115+
116+
== Decision
117+
118+
**We use Crossplane 2.x with Composition Functions** as the service orchestration mechanism for AppCat Framework 2.x.
119+
120+
=== Rationale
121+
122+
The decision prioritizes long-term maintainability and scalability over short-term simplicity:
123+
124+
1. **Built-in Revision Management**: Crossplane composition revisions provide native versioning and gradual rollout capabilities essential for maintenance workflows.
125+
Building equivalent functionality in a custom operator would be complex and require significant ongoing maintenance.
126+
127+
2. **Generic Function Pattern**: A single composition function handles all services by loading runtime configuration, eliminating the need for service-specific Go code.
128+
This enables Service Maintainers to add services independently while keeping the framework stable.
129+
130+
3. **Leverage Existing Ecosystem**: Crossplane providers (Helm, SQL) provide mature implementations for chart rendering, resource management, and status aggregation.
131+
Reimplementing these capabilities in a custom operator offers no clear benefit.
132+
133+
4. **Existing Team Knowledge**: The team is already proficient with Crossplane from component-appcat, reducing adoption risk and enabling faster implementation.
134+
135+
**Trade-off Accepted:** We accept higher abstraction complexity in exchange for built-in revision management, provider ecosystem leverage, and generic function scalability.
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
= ADR 0046 - Secret Management (Framework 2.0)
2+
:adr_author: Gabriel Saratura
3+
:adr_owner: Schedar
4+
:adr_reviewers: Schedar
5+
:adr_date:
6+
:adr_upd_date:
7+
:adr_status: draft
8+
:adr_tags: framework,framework2,secrets,crossplane,vault,openbao
9+
10+
include::partial$adr-meta.adoc[]
11+
12+
[NOTE]
13+
.Summary
14+
====
15+
The decision selects https://docs.crossplane.io/latest/guides/connection-details-composition/[Crossplane 2.x Connection Secret] as connection credentials
16+
====
17+
18+
== Problem
19+
20+
Services need to securely store and distribute connection credentials (passwords, endpoints, TLS certificates) to Service Users in their claim namespaces.
21+
22+
=== Solutions
23+
24+
==== Option A: Crossplane Connection Secrets (Current)
25+
26+
**Description:**
27+
28+
Crossplane's native https://docs.crossplane.io/latest/guides/connection-details-composition/[connection secret] mechanism.
29+
Composition functions use `AddConnectionDetails()` to export credentials, which Crossplane writes to the claim namespace.
30+
31+
**Advantages:**
32+
33+
* *Native Crossplane*: Built-in mechanism, no additional dependencies
34+
* *Proven Pattern*: Existing appcat repository uses this, though architectural simplification is required
35+
* *Automatic Propagation*: Crossplane handles secret creation in composite namespace
36+
* *Lifecycle Management*: Secrets deleted when composite deleted
37+
* *Simple Integration*: Servala portal fetches secrets directly via K8s API
38+
39+
**Disadvantages:**
40+
41+
* *No Rotation*: Secrets are static, no automatic rotation
42+
* *No Centralization*: Each service instance has separate secret
43+
* *Limited Auditing*: No built-in audit trail for secret access
44+
* *Complexity*: High complexity in handling connection secrets through managed resources
45+
46+
==== Option B: External Secrets Operator + Vault/OpenBao
47+
48+
**Description:**
49+
50+
Use https://github.com/openbao/openbao-secrets-operator[External Secrets Operator] to sync secrets from a centralized secret store (https://developer.hashicorp.com/vault[Vault] / https://openbao.org/[OpenBao]) to Kubernetes secrets.
51+
52+
**Architecture:**
53+
54+
[source]
55+
----
56+
Composition Function creates secret A in claim namespace with static content such as username
57+
58+
Creates ExternalSecret CR such as VaultDynamicSecret with reference to secret A
59+
60+
External Secrets Operator generates password and certificates
61+
62+
Syncs from Vault to secret A in claim namespace
63+
----
64+
65+
**Advantages:**
66+
67+
* *Centralized Management*: All secrets in one place (Vault/OpenBao)
68+
* *Rotation Support*: Can implement automatic password rotation
69+
* *Auditing*: Vault provides detailed audit logs
70+
* *RBAC*: Fine-grained access control policies
71+
* *Encryption at Rest*: Vault encrypts secrets
72+
73+
**Disadvantages:**
74+
75+
* *Additional Dependency*: Requires Vault/OpenBao + External Secrets Operator
76+
* *Additional Dependency*: Static content has to be handled outside the operator
77+
* *Complexity*: More moving parts, harder to debug
78+
* *Operational Overhead*: (Vault/OpenBao) requires management, backup, HA setup
79+
80+
== Decision
81+
82+
We use Crossplane Connection Secrets (continue current approach).
83+
84+
=== Rationale
85+
86+
1. *Proven Pattern*: Existing appcat repository uses Crossplane secret management though major simplification in secret propagation is necessary.
87+
88+
2. *Simplicity*: No additional dependencies. Crossplane handles lifecycle automatically.
89+
90+
3. *Service Credentials*: Secrets are service connection details (host, port, password). These don't require frequent rotation like API keys or tokens.
91+
92+
4. *Servala Integration*: Servala portal already fetches secrets via K8s API. No changes needed.
93+
94+
**Future Enhancement:**
95+
96+
* Phase 2: Investigate External Secrets Operator for centralized secret management
97+
* https://miro.com/app/board/uXjVJiczJH0=/?moveToWidget=3458764653515989765&cot=14[Implement rotation] policies for long-lived instances
98+
* Add audit trail for secret access
99+

0 commit comments

Comments
 (0)