Skip to content

Commit 7938669

Browse files
authored
Merge pull request #52 from Kuadrant/developer-portal-rbac
Developer portal new APIs
2 parents 1ab4c10 + 1c970cb commit 7938669

66 files changed

Lines changed: 10180 additions & 1153 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/verify.yml

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -59,16 +59,3 @@ jobs:
5959
- name: Verify generate command
6060
run: |
6161
make verify-generate
62-
verify-go-mod:
63-
name: Verify go.mod
64-
runs-on: ubuntu-latest
65-
steps:
66-
- name: Check out code
67-
uses: actions/checkout@v5
68-
- name: Setup Go
69-
uses: actions/setup-go@v5
70-
with:
71-
go-version-file: go.mod
72-
- name: Verify generate command
73-
run: |
74-
make verify-go-mod

AGENTS.md

Lines changed: 69 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,23 @@ This is a Kubernetes controller that reconciles Developer Portal capabilities ba
88

99
**Domain**: kuadrant.io
1010
**API Group**: devportal.kuadrant.io
11-
**Primary Resource**: APIProduct (v1alpha1)
11+
**Resources (v1alpha1)**:
12+
- `APIProduct`: Represents an API offering in the developer portal
13+
- `APIKey`: Represents a developer's request for API access (created in consumer namespace)
14+
- `APIKeyRequest`: Shadow resource for RBAC-based request review (created in owner namespace)
15+
- `APIKeyApproval`: API owner's decision to approve/deny access requests
16+
17+
## Design Documents
18+
19+
**RBAC Design**: The project implements namespace-based RBAC for API management. Read the full design at:
20+
https://github.com/Kuadrant/kuadrant-console-plugin/blob/main/docs/designs/2026-03-26-api-management-rbac-design.md
21+
22+
Key concepts from the design:
23+
- **Namespace isolation**: Consumers create APIKeys in their namespace, owners review APIKeyRequests in their namespace
24+
- **Shadow resources**: APIKeyRequest mirrors APIKey in owner's namespace for RBAC-enforced discovery
25+
- **Cross-namespace references**: APIKey references APIProduct across namespaces; APIKeyApproval references APIKey across namespaces
26+
- **Secret projection**: API key values projected to status field, eliminating need for secret read permissions
27+
- **Conditions pattern**: Uses conditions array (Pending/Approved/Denied/Failed) following CertificateSigningRequest pattern
1228

1329
## Development Commands
1430

@@ -47,25 +63,33 @@ make install # Install CRDs to cluster
4763
make deploy # Deploy controller to cluster
4864
make uninstall # Remove CRDs from cluster
4965
make undeploy # Remove controller from cluster
66+
make local-deploy # Deploy controller from current code
5067
```
5168

52-
### Bundle Operations (Operator Lifecycle Manager)
69+
### Build Utilities
5370
```bash
54-
make bundle # Generate bundle manifests
55-
make bundle-build # Build bundle image
71+
make build-installer # Generate consolidated YAML with CRDs and deployment
72+
make gateway-api-crds # Download Gateway API CRDs for testing
73+
make setup-test-e2e # Set up Kind cluster for e2e tests if it doesn't exist
74+
make cleanup-test-e2e # Tear down the Kind cluster used for e2e tests
5675
```
5776

5877
## Architecture
5978

6079
### Project Structure
6180
- **api/v1alpha1/**: Kubernetes API definitions
6281
- `apiproduct_types.go`: APIProduct CRD schema (Spec, Status, and List types)
82+
- `apikey_types.go`: APIKey CRD schema for consumer access requests
83+
- `apikeyrequest_types.go`: APIKeyRequest CRD schema for owner-side request review
84+
- `apikeyapproval_types.go`: APIKeyApproval CRD schema for approval decisions
6385
- `groupversion_info.go`: API group registration
6486
- `zz_generated.deepcopy.go`: Auto-generated DeepCopy methods
6587

6688
- **internal/controller/**: Reconciliation logic
67-
- `apiproduct_controller.go`: APIProductReconciler with Reconcile loop
68-
- Controller uses client.Client for K8s API access
89+
- `apiproduct_controller.go`: APIProductReconciler for API product lifecycle
90+
- `apikey_controller.go`: APIKeyReconciler for consumer API key requests
91+
- `apikeyrequest_controller.go`: APIKeyRequestReconciler for request processing
92+
- Controllers use client.Client for K8s API access
6993
- RBAC permissions defined via kubebuilder markers (`+kubebuilder:rbac`)
7094

7195
- **cmd/main.go**: Operator entry point
@@ -84,12 +108,30 @@ make bundle-build # Build bundle image
84108
- `config/prometheus/`: Prometheus ServiceMonitor
85109

86110
### Controller Pattern
87-
The operator follows the standard Kubernetes controller pattern:
88-
1. Watch APIProduct resources
89-
2. Reconcile loop called on create/update/delete events
90-
3. Compare desired state (Spec) vs actual state
91-
4. Take actions to converge actual state to desired state
92-
5. Update Status to reflect observed state
111+
The operator follows the standard Kubernetes controller pattern with multiple reconcilers:
112+
113+
**APIProductReconciler**:
114+
1. Watches APIProduct resources
115+
2. Discovers associated PlanPolicy and AuthPolicy from HTTPRoute
116+
3. Fetches and stores OpenAPI spec
117+
4. Updates status with discovered plans and auth scheme
118+
119+
**APIKeyReconciler**:
120+
1. Watches APIKey resources (consumer namespace)
121+
2. Creates APIKeyRequest shadow resource in owner namespace
122+
3. Processes APIKeyApproval decisions
123+
4. Creates API key secrets and projects values to status
124+
5. Updates conditions (Pending/Approved/Denied)
125+
126+
**APIKeyRequestReconciler**:
127+
1. Watches APIKeyRequest resources (owner namespace)
128+
2. Handles automatic approval mode
129+
3. Syncs status with related APIKey
130+
131+
All controllers:
132+
- Compare desired state (Spec) vs actual state
133+
- Take actions to converge actual state to desired state
134+
- Update Status to reflect observed state
93135

94136
### Key Dependencies
95137
- **controller-runtime v0.21.0**: Core controller framework
@@ -100,12 +142,22 @@ The operator follows the standard Kubernetes controller pattern:
100142
## Important Notes
101143

102144
### Modifying APIs
103-
1. Edit types in `api/v1alpha1/apiproduct_types.go`
145+
1. Edit types in `api/v1alpha1/*_types.go`
104146
2. Run `make manifests generate` to regenerate code and CRDs
105-
3. The APIProduct CRD currently has a placeholder `Foo` field - this should be replaced with actual fields
106-
107-
### Controller Implementation
108-
The reconciler in `internal/controller/apiproduct_controller.go` is scaffolded but not implemented. The Reconcile function needs business logic added.
147+
3. Update RBAC role definitions in `config/rbac/` if new permissions are needed
148+
4. Update sample manifests in `config/samples/` to reflect new fields
149+
150+
### RBAC Architecture
151+
The project implements namespace-based RBAC with three personas:
152+
- **API Consumer**: Creates APIKey in their namespace, can read their own APIKey status
153+
- **API Owner**: Views APIKeyRequest in their namespace, creates APIKeyApproval decisions
154+
- **API Admin**: Manages APIProduct resources and overall API catalog
155+
156+
Critical security invariants:
157+
- Consumers CANNOT see other consumers' APIKeys (namespace isolation)
158+
- Owners CANNOT see APIKey secrets (shadow resource pattern)
159+
- APIKeyApproval namespace MUST match APIProduct namespace (validated by controller)
160+
- API key values projected to status field (no secret read permissions required)
109161

110162
### Testing Environment
111163
- E2e tests use Kind cluster named `developer-portal-controller-test-e2e`

CLAUDE.md

Lines changed: 0 additions & 1 deletion
This file was deleted.

CLAUDE.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
AGENTS.md

Makefile

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -93,31 +93,44 @@ test: manifests generate fmt vet setup-envtest gateway-api-crds kuadrant-crds ##
9393
KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) --bin-dir $(LOCALBIN) -p path)" go test $$(go list ./... | grep -v /e2e) -coverprofile cover.out
9494

9595
# TODO(user): To use a different vendor for e2e tests, modify the setup under 'tests/e2e'.
96-
# CertManager is installed by default; skip with:
97-
# - CERT_MANAGER_INSTALL_SKIP=true
96+
# CertManager is installed by default; skip with: CERT_MANAGER_INSTALL_SKIP=true
97+
CERT_MANAGER_INSTALL_SKIP ?= true
9898
KIND_CLUSTER ?= developer-portal-controller-test-e2e
9999

100100
.PHONY: setup-test-e2e
101-
setup-test-e2e: ## Set up a Kind cluster for e2e tests if it does not exist
102-
@command -v $(KIND) >/dev/null 2>&1 || { \
103-
echo "Kind is not installed. Please install Kind manually."; \
104-
exit 1; \
105-
}
101+
setup-test-e2e: kind ## Set up a Kind cluster for e2e tests if it does not exist
106102
@case "$$($(KIND) get clusters)" in \
107103
*"$(KIND_CLUSTER)"*) \
108104
echo "Kind cluster '$(KIND_CLUSTER)' already exists. Skipping creation." ;; \
109105
*) \
110106
echo "Creating Kind cluster '$(KIND_CLUSTER)'..."; \
111107
$(KIND) create cluster --name $(KIND_CLUSTER) ;; \
112108
esac
109+
@echo ""
110+
@echo "installing developer portal controller APIs..."
111+
@$(MAKE) install
112+
@echo ""
113+
@echo "installing Gateway API APIs..."
114+
@$(MAKE) gateway-api-install
115+
@echo ""
116+
@echo "installing kuadrant core APIs..."
117+
@$(MAKE) kuadrant-core-install
118+
@echo ""
119+
@echo "cluster ready! Kuadrant and Gateway API APIs installed."
120+
@echo ""
113121

114122
.PHONY: test-e2e
115123
test-e2e: setup-test-e2e manifests generate fmt vet ## Run the e2e tests. Expected an isolated environment using Kind.
116-
KIND_CLUSTER=$(KIND_CLUSTER) go test ./test/e2e/ -v -ginkgo.v
117-
$(MAKE) cleanup-test-e2e
124+
@set +e; \
125+
PATH="$(LOCALBIN):$$PATH" \
126+
KIND_CLUSTER=$(KIND_CLUSTER) \
127+
CERT_MANAGER_INSTALL_SKIP=$(CERT_MANAGER_INSTALL_SKIP) \
128+
go test ./test/e2e/ -v -ginkgo.v; status=$$?; \
129+
$(MAKE) cleanup-test-e2e; \
130+
exit $$status
118131

119132
.PHONY: cleanup-test-e2e
120-
cleanup-test-e2e: ## Tear down the Kind cluster used for e2e tests
133+
cleanup-test-e2e: kind ## Tear down the Kind cluster used for e2e tests
121134
@$(KIND) delete cluster --name $(KIND_CLUSTER)
122135

123136
.PHONY: lint
@@ -208,7 +221,9 @@ undeploy: kustomize ## Undeploy controller from the K8s cluster specified in ~/.
208221
$(KUSTOMIZE) build config/default | $(KUBECTL) delete --ignore-not-found=$(ignore-not-found) -f -
209222

210223
.PHONY: local-deploy
211-
local-deploy: ## Deploy Kuadrant Operator from the current code
224+
local-deploy: ## Deploy Developer Portal controller from the current code
225+
@echo ""
226+
@echo "Deploy Developer Portal controller from the current code..."
212227
$(MAKE) docker-build IMG=$(IMAGE_TAG_BASE):dev
213228
$(MAKE) kind-load-image IMG=$(IMAGE_TAG_BASE):dev
214229
$(MAKE) deploy IMG=$(IMAGE_TAG_BASE):dev

PROJECT

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,4 +29,21 @@ resources:
2929
kind: APIKey
3030
path: github.com/kuadrant/developer-portal-controller/api/v1alpha1
3131
version: v1alpha1
32+
- api:
33+
crdVersion: v1
34+
namespaced: true
35+
domain: kuadrant.io
36+
group: devportal
37+
kind: APIKeyRequest
38+
path: github.com/kuadrant/developer-portal-controller/api/v1alpha1
39+
version: v1alpha1
40+
- api:
41+
crdVersion: v1
42+
namespaced: true
43+
controller: true
44+
domain: kuadrant.io
45+
group: devportal
46+
kind: APIKeyApproval
47+
path: github.com/kuadrant/developer-portal-controller/api/v1alpha1
48+
version: v1alpha1
3249
version: "3"

0 commit comments

Comments
 (0)