Skip to main content

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_user with member role
  • 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-lake on Ceph RADOS Gateway
  • Tenant APISIX Route: okustera-demo-route bound specifically to tenant-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"
username = "[email protected]"
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:
    1. Generates an immutable event ID: evt_<hex12>.
    2. Calculates the raw SHA-256 payload checksum.
    3. Partitions the event path by UTC date: events/YYYY-MM-DD/{event_id}.json.
    4. Uploads the JSON payload with S3 metadata headers directly to the webhook-lake bucket.

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:

  1. Real-Time Webhook Simulator: Fire synthetic Stripe, GitHub, or Shopify events directly from your browser to test API gateway routing and key verification.
  2. Data Lake Inspector: Inspect recently ingested events, verify SHA-256 signatures, and explore date-partitioned S3 storage objects in real time.
  3. Architecture Visualizer: View live status indicators for the Keystone tenant, Kubernetes namespace, APISIX route, and Ceph S3 bucket.