|
| 1 | +# Architecture Overview |
| 2 | + |
| 3 | +This document provides detailed architecture diagrams and explanations for how kube-bind handles resource synchronization between consumer and provider clusters, with a focus on isolation strategies and namespace mapping. |
| 4 | + |
| 5 | +## Cluster-Scoped Resources |
| 6 | + |
| 7 | +Cluster-scoped resources (like Sheriffs in our examples) can use different isolation strategies to control how they are placed on the provider cluster and how their associated secrets are isolated. |
| 8 | + |
| 9 | +### Architecture Flow |
| 10 | + |
| 11 | +``` |
| 12 | +CONSUMER CLUSTER PROVIDER CLUSTER (Backend) |
| 13 | +================ ========================== |
| 14 | +
|
| 15 | +┌─────────────────────────────┐ ┌────────────────────────────────────────────┐ |
| 16 | +│ Namespace: wild-west │ │ Contract Namespace (per consumer): │ |
| 17 | +│ - Secret: sheriff-badge- │──────────▶│ kube-bind-<consumer-id-hash> │ |
| 18 | +│ credentials (ref) │ Sync │ │ |
| 19 | +│ - Secret: sheriff-juris- │ │ APIServiceNamespace CR: │ |
| 20 | +│ diction-config (label) │ │ metadata.name: wild-west │ |
| 21 | +└─────────────────────────────┘ │ status.namespace: (varies by isolation)│ |
| 22 | + └────────────────────────────────────────────┘ |
| 23 | +┌─────────────────────────────┐ |
| 24 | +│ Sheriff: wyatt-earp │──────────▶ PROVIDER CLUSTER (varies by isolation) |
| 25 | +│ (cluster-scoped) │ Sync See isolation strategies below ↓ |
| 26 | +│ spec.intent │ |
| 27 | +│ spec.secretRefs: [...] │ |
| 28 | +└─────────────────────────────┘ |
| 29 | + ▲ |
| 30 | + │ Status updates |
| 31 | + └──────────────────────────────── |
| 32 | +``` |
| 33 | + |
| 34 | +### Isolation Strategies for Cluster-Scoped Resources |
| 35 | + |
| 36 | +#### 1. IsolationPrefixed |
| 37 | + |
| 38 | +``` |
| 39 | +┌────────────────────────────────────────────┐ |
| 40 | +│ [cc-prefixed] IsolationPrefixed: │ |
| 41 | +│ Sheriff: <consumer-id>-wyatt-earp │ |
| 42 | +│ (cluster-scoped, prefixed name) │ |
| 43 | +│ Secrets in namespace: │ |
| 44 | +│ kube-bind-<consumer-id>-wild-west │ |
| 45 | +│ (APIServiceNamespace mapping) │ |
| 46 | +└────────────────────────────────────────────┘ |
| 47 | +``` |
| 48 | + |
| 49 | +**How it works:** |
| 50 | +- Sheriff resource name is prefixed with consumer ID: `<consumer-id>-wyatt-earp` |
| 51 | +- Sheriff remains cluster-scoped on provider |
| 52 | +- Secrets are isolated via namespace mapping: `wild-west` → `kube-bind-<consumer-id>-wild-west` |
| 53 | +- Multiple consumers can safely coexist with different prefixed resources |
| 54 | + |
| 55 | +**Use case:** When you want cluster-scoped resources on the provider but need to support multiple consumers safely. |
| 56 | + |
| 57 | +#### 2. IsolationNone |
| 58 | + |
| 59 | +``` |
| 60 | +┌────────────────────────────────────────────┐ |
| 61 | +│ [cc-none] IsolationNone: │ |
| 62 | +│ Sheriff: wyatt-earp │ |
| 63 | +│ (cluster-scoped, same name on both!) │ |
| 64 | +│ ⚠ Last write wins between consumers │ |
| 65 | +│ Secrets in namespace: wild-west │ |
| 66 | +│ (Same namespace name on both sides!) │ |
| 67 | +└────────────────────────────────────────────┘ |
| 68 | +``` |
| 69 | + |
| 70 | +**How it works:** |
| 71 | +- Sheriff resource keeps the same name on both sides: `wyatt-earp` |
| 72 | +- Sheriff remains cluster-scoped on provider |
| 73 | +- **Namespace mapping is 1:1**: `wild-west` → `wild-west` (same name!) |
| 74 | +- **⚠️ WARNING:** No isolation! Multiple consumers share the same namespace. This is possible but discouraged. |
| 75 | +- Last write wins if multiple consumers create the same resource |
| 76 | + |
| 77 | +**Use case:** Single consumer scenarios or when you explicitly want shared resources. **Not recommended for multi-consumer environments.** |
| 78 | + |
| 79 | +#### 3. IsolationNamespaced |
| 80 | + |
| 81 | +``` |
| 82 | +┌────────────────────────────────────────────┐ |
| 83 | +│ [cc-namespaced] IsolationNamespaced: │ |
| 84 | +│ Sheriff: wyatt-earp │ |
| 85 | +│ (CRD toggled to NamespaceScoped) │ |
| 86 | +│ in namespace: │ |
| 87 | +│ kube-bind-<consumer-id>-wild-west │ |
| 88 | +│ Secrets in same namespace (isolated) │ |
| 89 | +└────────────────────────────────────────────┘ |
| 90 | +``` |
| 91 | + |
| 92 | +**How it works:** |
| 93 | +- Sheriff CRD is toggled from ClusterScoped to NamespaceScoped on the provider |
| 94 | +- Sheriff is placed in consumer-specific namespace: `kube-bind-<consumer-id>-wild-west` |
| 95 | +- Secrets are in the same namespace (isolated) |
| 96 | +- Namespace mapping: `wild-west` → `kube-bind-<consumer-id>-wild-west` |
| 97 | + |
| 98 | +**Use case:** When you want the strongest isolation and are willing to modify the CRD scope on the provider. |
| 99 | + |
| 100 | +### Key Insights for Cluster-Scoped Resources |
| 101 | + |
| 102 | +The isolation strategy determines **BOTH** how Sheriff resources are placed **AND** how namespaces are mapped: |
| 103 | + |
| 104 | +- **IsolationNone**: Same namespace name on both sides (`wild-west` → `wild-west`) |
| 105 | + - ⚠️ **NO isolation for secrets!** Multiple consumers can exist but share the same namespace (discouraged) |
| 106 | + |
| 107 | +- **IsolationPrefixed**: Sheriff name prefixed, secrets isolated via namespace mapping |
| 108 | + - (`wild-west` → `kube-bind-<consumer-id>-wild-west`) |
| 109 | + |
| 110 | +- **IsolationNamespaced**: Sheriff becomes namespaced, secrets isolated in same namespace |
| 111 | + - (`wild-west` → `kube-bind-<consumer-id>-wild-west`) |
| 112 | + |
| 113 | +## Namespaced Resources |
| 114 | + |
| 115 | +Namespaced resources (like Cowboys in our examples) always use the ServiceNamespaced isolation strategy, regardless of the isolation parameter. |
| 116 | + |
| 117 | +### Architecture Flow |
| 118 | + |
| 119 | +``` |
| 120 | +CONSUMER CLUSTER PROVIDER CLUSTER (Backend) |
| 121 | +================ ========================== |
| 122 | +
|
| 123 | +┌─────────────────────────────┐ ┌────────────────────────────────────────────┐ |
| 124 | +│ Namespace: wild-west │ │ Contract Namespace (per consumer): │ |
| 125 | +│ │──────────▶│ kube-bind-<consumer-id-hash> │ |
| 126 | +│ Cowboy: billy-the-kid │ Sync │ │ |
| 127 | +│ (namespaced) │ │ APIServiceNamespace CR: │ |
| 128 | +│ spec.intent │ │ metadata.name: wild-west │ |
| 129 | +│ spec.secretRefs: [...] │ │ status.namespace: ─────────────────┐ │ |
| 130 | +│ │ │ kube-bind-<consumer-id>-wild-west│ │ |
| 131 | +│ - Secret: colt-45-permit │ └────────────────────────────────────────┼───┘ |
| 132 | +│ (ref) │ │ |
| 133 | +│ - Secret: cowboy-gang- │ ACTUAL PROVIDER NAMESPACE: ◀─────────────┘ |
| 134 | +│ affiliation (label) │ ┌────────────────────────────────────────────┐ |
| 135 | +└─────────────────────────────┘ │ Namespace: kube-bind-<consumer-id>-wild- │ |
| 136 | + ▲ │ west │ |
| 137 | + │ Status updates │ │ |
| 138 | + └────────────────────────────────│ Cowboy: billy-the-kid (namespaced) │ |
| 139 | + │ - Secret: colt-45-permit │ |
| 140 | + │ - Secret: cowboy-gang-affiliation │ |
| 141 | + │ │ |
| 142 | + │ RBAC (for secret access): │ |
| 143 | + │ - Role: kube-binder-export-wild-west- │ |
| 144 | + │ cowboys (in this namespace) │ |
| 145 | + │ - RoleBinding: ... (in this namespace) │ |
| 146 | + └────────────────────────────────────────────┘ |
| 147 | +``` |
| 148 | + |
| 149 | +### InformerScope Variations |
| 150 | + |
| 151 | +Namespaced resources support two informer scope configurations: |
| 152 | + |
| 153 | +#### 1. NamespacedScope (nn) |
| 154 | + |
| 155 | +``` |
| 156 | +┌────────────────────────────────────────────────────────────────────┐ |
| 157 | +│ [nn] Namespaced → Namespaced (informerScope=NamespacedScope): │ |
| 158 | +│ - Consumer: Cowboy in namespace wild-west │ |
| 159 | +│ - Provider: Cowboy in namespace kube-bind-<id>-wild-west │ |
| 160 | +│ - Konnector watches only its own namespace on provider side │ |
| 161 | +│ - Natural isolation via namespaces │ |
| 162 | +└────────────────────────────────────────────────────────────────────┘ |
| 163 | +``` |
| 164 | + |
| 165 | +**How it works:** |
| 166 | +- Consumer resource is namespaced: `wild-west/billy-the-kid` |
| 167 | +- Provider resource is namespaced: `kube-bind-<consumer-id>-wild-west/billy-the-kid` |
| 168 | +- Konnector watches **only its own namespace** on the provider side |
| 169 | +- RBAC uses namespace-scoped Roles and RoleBindings |
| 170 | + |
| 171 | +**Use case:** Most efficient for namespaced resources, minimizes RBAC and watch scope. |
| 172 | + |
| 173 | +#### 2. ClusterScope (nc) |
| 174 | + |
| 175 | +``` |
| 176 | +┌────────────────────────────────────────────────────────────────────┐ |
| 177 | +│ [nc] Namespaced → Cluster (informerScope=ClusterScope): │ |
| 178 | +│ - Consumer: Cowboy in namespace wild-west │ |
| 179 | +│ - Provider: Cowboy in namespace kube-bind-<id>-wild-west │ |
| 180 | +│ - Konnector watches cluster-wide on provider side │ |
| 181 | +│ - Still isolated via namespaces, but different RBAC model │ |
| 182 | +└────────────────────────────────────────────────────────────────────┘ |
| 183 | +``` |
| 184 | + |
| 185 | +**How it works:** |
| 186 | +- Consumer resource is namespaced: `wild-west/billy-the-kid` |
| 187 | +- Provider resource is namespaced: `kube-bind-<consumer-id>-wild-west/billy-the-kid` |
| 188 | +- Konnector watches **cluster-wide** on the provider side |
| 189 | +- RBAC uses ClusterRoles and ClusterRoleBindings |
| 190 | + |
| 191 | +**Use case:** When you need cluster-wide visibility or have specific RBAC requirements. |
| 192 | + |
| 193 | +### Key Insights for Namespaced Resources |
| 194 | + |
| 195 | +Namespaced resources **ALWAYS** use the ServiceNamespaced isolation strategy: |
| 196 | + |
| 197 | +- The `isolation` parameter **only applies to cluster-scoped resources** |
| 198 | +- Each consumer gets its own provider namespace via APIServiceNamespace mapping |
| 199 | +- Namespace mapping is always: `wild-west` → `kube-bind-<consumer-id>-wild-west` |
| 200 | +- The `informerScope` parameter only affects whether the konnector watches at namespace or cluster level |
| 201 | +- Secrets are always isolated per consumer |
| 202 | + |
| 203 | +## Complete Test Lifecycle |
| 204 | + |
| 205 | +The end-to-end test validates the complete binding and synchronization flow: |
| 206 | + |
| 207 | +### Setup Phase |
| 208 | + |
| 209 | +1. **Create provider workspace (KCP)** |
| 210 | + - Install kube-bind CRDs |
| 211 | + - Start backend server (HTTP API for binding) |
| 212 | + - Bootstrap example CRDs (Cowboys/Sheriffs) |
| 213 | + - Apply templates (template-cowboys.yaml / template-sheriffs.yaml) |
| 214 | + |
| 215 | +2. **Create consumer workspaces** |
| 216 | + - Start konnector on each consumer (watches APIServiceBinding) |
| 217 | + |
| 218 | +### Binding Phase |
| 219 | + |
| 220 | +3. **Login to provider** |
| 221 | + - Simulate browser auth flow |
| 222 | + - Save credentials to kube-bind-config.yaml |
| 223 | + |
| 224 | +4. **List templates & collections** |
| 225 | + - Verify backend returns templates |
| 226 | + - Verify collections exist |
| 227 | + |
| 228 | +5. **Bind API** |
| 229 | + - Send BindableResourcesRequest with templateRef and clusterIdentity |
| 230 | + - Backend creates on provider: |
| 231 | + - Contract namespace: `kube-bind-<consumer-id-hash>` |
| 232 | + - APIServiceExportRequest |
| 233 | + - APIServiceNamespace: `wild-west` → `kube-bind-<id>-wild-west` |
| 234 | + - RBAC resources for secret access |
| 235 | + - Backend returns BindingResourceResponse with CRDs, RBAC manifests |
| 236 | + |
| 237 | +6. **Apply binding on consumer** |
| 238 | + - Create APIServiceBinding (watched by konnector) |
| 239 | + - Apply CRDs |
| 240 | + - Wait for CRD to be established |
| 241 | + |
| 242 | +### Synchronization & Verification Phase |
| 243 | + |
| 244 | +7. **Create instance on consumer** |
| 245 | + - Create resource (Cowboy or Sheriff) in namespace `wild-west` |
| 246 | + - Create associated secrets |
| 247 | + |
| 248 | +8. **Verify sync to provider** |
| 249 | + - Instance created on provider with ClusterNamespaceAnnotation |
| 250 | + - Extract providerContractNamespace and providerObjectNamespace |
| 251 | + |
| 252 | +9. **Verify namespace pre-seeding & RBAC** |
| 253 | + - APIServiceNamespace created with status.namespace populated |
| 254 | + - Actual provider namespace exists |
| 255 | + - RBAC resources created (ClusterRole/Role + Bindings) |
| 256 | + |
| 257 | +10. **Test bi-directional sync** |
| 258 | + - Update spec on consumer → synced to provider |
| 259 | + - Update status on provider → synced to consumer |
| 260 | + |
| 261 | +11. **Verify secret sync** |
| 262 | + - Referenced secret (spec.secretRefs) synced to provider namespace |
| 263 | + - Label-selected secret synced |
| 264 | + |
| 265 | +12. **Test deletion** |
| 266 | + - Delete on consumer → deleted on provider |
| 267 | + |
| 268 | +### Multi-Consumer Isolation Verification |
| 269 | + |
| 270 | +Running with 2 consumers validates: |
| 271 | +- Each consumer gets isolated contract namespace |
| 272 | +- Each consumer gets isolated provider namespace (except with IsolationNone) |
| 273 | +- Secrets are properly isolated (except with IsolationNone) |
| 274 | +- For cluster-scoped resources with IsolationNone, last-write-wins |
| 275 | +- For cluster-scoped resources with IsolationPrefixed, resources are name-prefixed |
| 276 | +- For cluster-scoped resources with IsolationNamespaced, provider CRD is toggled to NamespaceScoped |
| 277 | + |
| 278 | +## Implementation Details |
| 279 | + |
| 280 | +### Code Structure |
| 281 | + |
| 282 | +- **Isolation strategies**: `pkg/konnector/controllers/cluster/serviceexport/isolation/` |
| 283 | + - `none.go`: No isolation, same names/namespaces on both sides |
| 284 | + - `prefixed.go`: Prefix resource names with consumer ID |
| 285 | + - `namespaced.go`: Convert cluster-scoped to namespaced |
| 286 | + - `servicenamespaced.go`: APIServiceNamespace-based mapping for namespaced resources |
| 287 | + |
| 288 | +- **Controller logic**: `pkg/konnector/controllers/cluster/serviceexport/serviceexport_reconcile.go` |
| 289 | + - Determines which isolation strategy to use based on resource scope and configuration |
| 290 | + - Lines 247-284: Strategy selection logic |
| 291 | + |
| 292 | +### Strategy Selection Logic |
| 293 | + |
| 294 | +```go |
| 295 | +switch { |
| 296 | +case schema.Spec.Scope == apiextensionsv1.NamespaceScoped: |
| 297 | + // ALWAYS use ServiceNamespaced for namespaced resources |
| 298 | + isolationStrategy = isolation.NewServiceNamespaced(...) |
| 299 | + |
| 300 | +case export.Spec.Isolation == kubebindv1alpha2.IsolationNone: |
| 301 | + isolationStrategy = isolation.NewNone(...) |
| 302 | + |
| 303 | +case export.Spec.Isolation == kubebindv1alpha2.IsolationPrefixed: |
| 304 | + isolationStrategy = isolation.NewPrefixed(...) |
| 305 | + |
| 306 | +case export.Spec.Isolation == kubebindv1alpha2.IsolationNamespaced: |
| 307 | + isolationStrategy = isolation.NewNamespaced(...) |
| 308 | +} |
| 309 | +``` |
| 310 | + |
| 311 | +## References |
| 312 | + |
| 313 | +- Test implementation: `test/e2e/bind/happy-case_test.go` |
| 314 | +- API types: `sdk/apis/kubebind/v1alpha2/apiserviceexport_types.go` |
| 315 | +- Isolation strategies: `pkg/konnector/controllers/cluster/serviceexport/isolation/` |
0 commit comments