Run the Migration Template
In this module you will use the "Migrate from 3scale to Connectivity Link" Software Template in Developer Hub to migrate one of the pre-deployed 3scale applications.
This page covers both the user experience (how to run it) and the template internals (what it generates and why).
How the template works
The migration template is a Backstage Scaffolder template that:
-
Accepts parameters — application name, source/target namespaces, auth model, backend service details
-
Generates Kubernetes manifests — all Connectivity Link CRDs needed to replace 3scale
-
Publishes to Gitea — pushes the manifests to a new Git repository
-
Creates an ArgoCD Application — GitOps syncs the manifests to the target namespace
-
Registers in Catalog — adds the migrated component to Developer Hub
-
Sends a notification — emails the user about the completed migration
Template parameters
The template has three parameter groups:
# Group 1: Application Details
parameters:
- title: Application Details
properties:
appName: # e.g. neuralbank-backend
owner: # e.g. user1
authModel: # oidc | apikey
# Group 2: Source & Target Configuration
- title: Source & Target Configuration
properties:
sourceNamespace: # e.g. neuralbank-3scale
targetNamespace: # e.g. user1-neuralbank-cl
backendServiceName: # e.g. neuralbank-backend-svc
backendServicePort: # e.g. 8080
# Group 3: Authentication & Rate Limiting
- title: Authentication & Rate Limiting
properties:
keycloakRealm: # e.g. neuralbank (OIDC only)
keycloakClientId: # e.g. neuralbank-frontend (OIDC only)
rateLimitPerMinute: # e.g. 60
What the template generates
The skeleton generates 7 Kubernetes manifests plus a catalog registration:
| File | Resource | What it replaces from 3scale |
|---|---|---|
|
Gateway |
The APIcast gateway entry point |
|
HTTPRoute |
MappingRules from the Backend CRD |
|
AuthPolicy |
The Product authentication config (OIDC or API Key) |
|
RateLimitPolicy |
Application Plan rate limits |
|
PlanPolicy |
Application Plan tiers (free/basic/pro) |
|
APIProduct |
3scale Developer Portal + ActiveDoc |
|
Route |
OpenShift Route to the new Gateway |
|
Backstage entities |
Catalog registration with Kuadrant annotations |
Generated Gateway
Replaces the APIcast gateway with a standard Kubernetes Gateway API resource:
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: neuralbank-backend-gateway
namespace: user1-neuralbank-cl
annotations:
networking.istio.io/service-type: ClusterIP
kuadrant.io/namespace: kuadrant-system
spec:
gatewayClassName: istio
listeners:
- name: http
port: 8080
protocol: HTTP
allowedRoutes:
namespaces:
from: Same
Generated HTTPRoute (replaces MappingRules)
The 3scale MappingRules that map GET /api/v1/customers → hits metric become standard HTTPRoute path matching:
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: neuralbank-backend-route
namespace: user1-neuralbank-cl
spec:
parentRefs:
- name: neuralbank-backend-gateway
rules:
- matches:
- path:
type: PathPrefix
value: /api
backendRefs:
- name: neuralbank-backend-svc
port: 8080
- matches:
- path:
type: PathPrefix
value: /q
backendRefs:
- name: neuralbank-backend-svc
port: 8080
Generated AuthPolicy (replaces Product auth config)
The template uses Nunjucks conditionals to generate different auth configurations based on the authModel parameter:
For OIDC — replaces Product.spec.deployment.apicastHosted.authentication.oidc:
apiVersion: kuadrant.io/v1
kind: AuthPolicy
spec:
targetRef:
kind: HTTPRoute
name: neuralbank-backend-route
defaults:
rules:
authentication:
oidc:
jwt:
issuerUrl: https://rhbk.{cluster_domain}/realms/neuralbank
credentials:
authorizationHeader:
prefix: Bearer
For API Key — replaces Product.spec.deployment.apicastHosted.authentication.userkey:
apiVersion: kuadrant.io/v1
kind: AuthPolicy
spec:
targetRef:
kind: HTTPRoute
name: nfl-wallet-route
defaults:
rules:
authentication:
api-key-auth:
apiKey:
selector:
matchLabels:
app: nfl-wallet
kuadrant.io/apikey: "true"
credentials:
customHeader:
name: X-API-Key
In 3scale, API Keys are user_key query parameters stored in the 3scale database. In Connectivity Link, they become X-API-Key headers validated against Kubernetes Secrets with labels — fully GitOps-manageable.
|
Generated RateLimitPolicy (replaces Application Plan limits)
The 3scale Application Plan limits[].period: minute, value: 60 becomes:
apiVersion: kuadrant.io/v1
kind: RateLimitPolicy
spec:
targetRef:
kind: HTTPRoute
name: neuralbank-backend-route
limits:
global:
rates:
- limit: 60
window: 1m
Generated PlanPolicy (replaces Application Plan tiers)
The 3scale basic/premium plans with different limits become a PlanPolicy with CEL predicates:
apiVersion: extensions.kuadrant.io/v1alpha1
kind: PlanPolicy
spec:
targetRef:
kind: HTTPRoute
name: neuralbank-backend-route
plans:
- tier: free
predicate: |
auth.identity.metadata.annotations["secret.kuadrant.io/plan-id"] == "free"
limits:
daily: 100
custom:
- limit: 10
window: "1m"
- tier: basic
predicate: |
auth.identity.metadata.annotations["secret.kuadrant.io/plan-id"] == "basic"
limits:
daily: 1000
custom:
- limit: 60
window: "1m"
- tier: pro
predicate: |
auth.identity.metadata.annotations["secret.kuadrant.io/plan-id"] == "pro"
limits:
daily: 10000
custom:
- limit: 300
window: "1m"
Generated APIProduct (replaces 3scale Developer Portal + ActiveDoc)
apiVersion: devportal.kuadrant.io/v1alpha1
kind: APIProduct
spec:
displayName: "neuralbank-backend — Migrated from 3scale"
publishStatus: Published
approvalMode: automatic
tags: [migrated, 3scale-to-cl, oidc, kuadrant]
documentation:
openAPISpecURL: "https://neuralbank-backend.{cluster_domain}/q/openapi"
swaggerUI: "https://neuralbank-backend.{cluster_domain}/q/swagger-ui"
targetRef:
kind: HTTPRoute
name: neuralbank-backend-route
Template scaffolder steps
After the manifests are generated, the template executes these steps:
Step 1: fetch:template
└─ Generates 7 manifests + catalog-info.yaml from the skeleton
Step 2: publish:gitea
└─ Pushes to: gitea-gitea.{cluster_domain}/ws-user1/neuralbank-backend-cl-migration
Step 3: catalog:register
└─ Registers the Component and API entities in the Developer Hub catalog
Step 4: http:backstage:request (ArgoCD)
└─ Creates an ArgoCD Application:
- name: user1-neuralbank-backend-cl-migration
- source: the Gitea repo / manifests path
- destination: user1-neuralbank-cl namespace
- syncPolicy: automated, selfHeal, CreateNamespace
Step 5: http:backstage:request (Notification)
└─ Sends email via Mailpit with migration summary
Step-by-step: Run the migration
1. Open Developer Hub
Navigate to https://backstage-developer-hub-developer-hub.apps.cluster.example.com and click Create.
3. Fill in the parameters
For this exercise, migrate the Neuralbank backend from 3scale OIDC:
| Parameter | Value |
|---|---|
Application Name |
|
Owner |
|
Authentication Model |
|
Source Namespace (3scale) |
|
Target Namespace (CL) |
|
Backend Service Name |
|
Backend Service Port |
|
Keycloak Realm |
|
Keycloak Client ID |
|
Rate Limit (per minute) |
|
To migrate NFL Wallet instead, select apikey as auth model, use nfl-wallet-3scale as source, user1-nfl-wallet-cl as target, nfl-wallet-api as backend service, and 120 as rate limit.
|
4. Review and create
Click Create. Watch the scaffolder progress — each step shows a green checkmark when complete.
3scale → Connectivity Link resource mapping summary
3scale Product ──→ AuthPolicy + RateLimitPolicy + PlanPolicy 3scale Backend ──→ HTTPRoute (path matching replaces MappingRules) 3scale ActiveDoc ──→ APIProduct (publishes OpenAPI in Developer Hub) 3scale Application Plan ──→ PlanPolicy (CEL predicates on Secret annotations) 3scale Developer Portal ──→ Kuadrant Backstage plugin (APIProduct discovery) APIcast Gateway ──→ Istio Gateway (Gateway API standard) 3scale Admin UI ──→ Git + ArgoCD (full GitOps)
In the next module you will verify the migrated application and compare it with the 3scale original.