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:

  1. Accepts parameters — application name, source/target namespaces, auth model, backend service details

  2. Generates Kubernetes manifests — all Connectivity Link CRDs needed to replace 3scale

  3. Publishes to Gitea — pushes the manifests to a new Git repository

  4. Creates an ArgoCD Application — GitOps syncs the manifests to the target namespace

  5. Registers in Catalog — adds the migrated component to Developer Hub

  6. 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.yaml

Gateway

The APIcast gateway entry point

httproute.yaml

HTTPRoute

MappingRules from the Backend CRD

authpolicy.yaml

AuthPolicy

The Product authentication config (OIDC or API Key)

ratelimitpolicy.yaml

RateLimitPolicy

Application Plan rate limits

planpolicy.yaml

PlanPolicy

Application Plan tiers (free/basic/pro)

apiproduct.yaml

APIProduct

3scale Developer Portal + ActiveDoc

route.yaml

Route

OpenShift Route to the new Gateway

catalog-info.yaml

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/customershits 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.

2. Select the template

Find and select "Migrate from 3scale to Connectivity Link".

3. Fill in the parameters

For this exercise, migrate the Neuralbank backend from 3scale OIDC:

Parameter Value

Application Name

neuralbank-backend

Owner

user1

Authentication Model

oidc

Source Namespace (3scale)

neuralbank-3scale

Target Namespace (CL)

user1-neuralbank-cl

Backend Service Name

neuralbank-backend-svc

Backend Service Port

8080

Keycloak Realm

neuralbank

Keycloak Client ID

neuralbank-frontend

Rate Limit (per minute)

60

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.

5. Verify in ArgoCD

Open ArgoCD and confirm the new application is Synced and Healthy:

https://openshift-gitops-server-openshift-gitops.{cluster_domain}

6. Verify the generated resources

oc get gateway,httproute,authpolicy,ratelimitpolicy,planpolicy,apiproduct \
  -n {user_name}-neuralbank-cl

All resources should exist and show healthy status.

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.