End-to-End Tenant Demo: Webhook Data Lake
This guide walks through a production-grade, end-to-end tenant application deployed entirely via Infrastructure-as-Code (IaC) using the Okustera Terraform Provider.
A live, publicly accessible instance of this workload is published at:
🌐 Live Demo Portal: https://demo.okustera.com
The source repository for this demo is located locally at terraform-tenant-demo/.
1. Architectural Overview
The demo provisions an isolated tenant environment, sets up a serverless event ingestion engine behind an API Gateway, and continuously streams partitioned JSON events into an S3-compatible data lake backed by Ceph RADOS:
2. Multi-Tenancy & Isolation Boundaries
All components of this workload run inside dedicated tenant-scoped primitives:
- Keystone Project / Tenant:
demo_tenant(dedicated tenant project) - Tenant User:
demo_userwithmemberrole - Tenant Kubernetes Cluster:
tenant-k8s-cluster-ubuntu(Managed via OpenStack Magnum) - Tenant Workload Namespace:
tenant-demo(isolated from system pods) - Tenant Object Storage: Bucket
webhook-lakeon Ceph RADOS Gateway - Tenant APISIX Route:
okustera-demo-routebound specifically totenant-demo
3. Terraform Implementation Walkthrough
The demo infrastructure is defined declaratively across two Terraform modules:
3.1 Keystone Tenant & User Provisioning (main.tf)
terraform {
required_providers {
okustera = {
source = "okustera/okustera"
}
}
}
provider "okustera" {
endpoint = "http://portal.okustera.com/api/v1"
password = var.admin_password
}
# 1. Provision dedicated tenant project
resource "okustera_tenant" "demo_tenant" {
name = "tenant_enterprise_prod"
description = "Enterprise Production Tenant"
enabled = true
}
# 2. Provision tenant user
resource "okustera_user" "demo_user" {
name = "demo_user"
password = var.demo_user_password
default_project_id = okustera_tenant.demo_tenant.id
enabled = true
}
# 3. Assign member RBAC role
resource "okustera_role_assignment" "demo_user_member" {
project_id = okustera_tenant.demo_tenant.id
user_id = okustera_user.demo_user.id
role_name = "member"
}
3.2 S3 Data Lake & Workload Ingestion Pipeline (webhook_pipeline.tf)
# 1. Provision Ceph S3 Bucket for the Data Lake
resource "okustera_s3_bucket" "webhook_lake" {
name = "webhook-lake"
acl = "private"
}
# 2. Deploy Tenant Namespace, Secrets, Pods & APISIX Route
resource "terraform_data" "webhook_pipeline_workload" {
depends_on = [okustera_s3_bucket.webhook_lake]
provisioner "local-exec" {
command = <<-EOT
cat << 'YAML' | kubectl --kubeconfig=${var.kubeconfig_tenant} apply -f -
apiVersion: v1
kind: Namespace
metadata:
name: tenant-demo
---
apiVersion: v1
kind: Secret
metadata:
name: omc-webhook-demo-secrets
namespace: tenant-demo
type: Opaque
stringData:
S3_ENDPOINT_URL: "${var.s3_endpoint}"
S3_ACCESS_KEY_ID: "${var.s3_access_key}"
S3_SECRET_ACCESS_KEY: "${var.s3_secret_key}"
S3_BUCKET_NAME: "webhook-lake"
WEBHOOK_API_KEY: "${var.api_key}"
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: omc-webhook-demo
namespace: tenant-demo
spec:
replicas: 1
selector:
matchLabels:
app: omc-webhook-demo
template:
metadata:
labels:
app: omc-webhook-demo
spec:
containers:
- name: webhook-engine
image: omc-webhook-demo:latest
ports:
- containerPort: 8080
envFrom:
- secretRef:
name: omc-webhook-demo-secrets
---
apiVersion: apisix.apache.org/v2
kind: ApisixRoute
metadata:
name: okustera-demo-route
namespace: tenant-demo
spec:
http:
- name: web-and-api
match:
hosts:
- demo.okustera.com
paths:
- "/*"
backends:
- serviceName: omc-webhook-demo
servicePort: 8080
YAML
EOT
}
}
4. Security Model: API Key Authentication & Event Partitioning
4.1 Ingestion Security & Validation
Incoming webhook POST requests to /api/v1/webhooks must supply the configured API key via the X-API-Key header (or Authorization: Bearer <token>).
- Unauthenticated Requests (
401 Unauthorized): If the header is missing or invalid, the ingestion engine immediately aborts execution. - Authenticated Requests (
202 Accepted): Upon validation, the engine:- Generates an immutable event ID:
evt_<hex12>. - Calculates the raw SHA-256 payload checksum.
- Partitions the event path by UTC date:
events/YYYY-MM-DD/{event_id}.json. - Uploads the JSON payload with S3 metadata headers directly to the
webhook-lakebucket.
- Generates an immutable event ID:
4.2 Sample Ingestion Request & Response
curl -X POST https://demo.okustera.com/api/v1/webhooks \
-H "Content-Type: application/json" \
-H "X-API-Key: <YOUR_WEBHOOK_API_KEY>" \
-d '{
"event": "invoice.paid",
"customer": "cust_enterprise_88",
"amount": 2450.00,
"currency": "EUR"
}'
Response (HTTP 202 Accepted):
{
"status": "accepted",
"event_id": "evt_94b1ef8a3c10",
"event_type": "invoice.paid",
"s3_bucket": "webhook-lake",
"partition_key": "events/2026-09-15/evt_94b1ef8a3c10.json",
"s3_stored": true,
"received_at": "2026-09-15T14:50:00.000000+00:00",
"signature_valid": true
}
5. Live Interactive UI & Webhook Simulator
Navigating to https://demo.okustera.com provides an interactive web interface:
- Real-Time Webhook Simulator: Fire synthetic Stripe, GitHub, or Shopify events directly from your browser to test API gateway routing and key verification.
- Data Lake Inspector: Inspect recently ingested events, verify SHA-256 signatures, and explore date-partitioned S3 storage objects in real time.
- Architecture Visualizer: View live status indicators for the Keystone tenant, Kubernetes namespace, APISIX route, and Ceph S3 bucket.