Gateway migration example¶
The fully-annotated, ready-to-copy gateway-migration.yaml manifest — every
field, with comments. For the field-by-field meaning of everything here, see
the gateway manifest reference.
Also available as a plain file at
gateway-examples/gateway-migration.yaml
— that link downloads the raw file rather than opening it in-browser, since
it's YAML, not a doc page.
# yaml-language-server: $schema=https://raw.githubusercontent.com/confluentinc/kcp/main/internal/manifest/gatewaymigration.schema.json
#
# The declarative form of a `kcp migration` run. One file replaces the topology
# and auth flags that used to be repeated across `init` and `execute`.
#
# kcp migration init --migration-yaml gateway-migration.yaml
# kcp migration execute --migration-yaml gateway-migration.yaml --migration-state-file migration-state.json
# kcp migration lag-check --migration-yaml gateway-migration.yaml
#
# Paths in this file resolve relative to the PROCESS working directory, not to
# the file's own location. The one exception is spec.gateway.kubeconfig, where a
# leading ~/ is expanded.
#
# This file is secret-bearing when credentials are written inline. Keep it 0600,
# and prefer ${ENV_VAR} references (below) over literal passwords.
apiVersion: kcp.confluent.io/v1alpha1
kind: GatewayMigration
metadata:
# THE migration identity — unique per state file, and written into the state
# file's migration_id. Pre-existing uuid-keyed migrations keep working and
# stay addressable with `--migration-id`.
name: msk-prod-to-cc-batch-1
# Opt in to ${ENV_VAR} resolution for THIS file. Absent (the default) means
# every value is literal. Each file governs itself: this does not reach into a
# referenced credentials file, which needs its own `interpolate: true`.
#
# Syntax is narrow on purpose: only ${VAR} is a reference (a bare $VAR is not),
# `$${` escapes a literal "${", and an unset variable is a hard error naming the
# variable — an empty password silently attempting auth is the worst outcome.
interpolate: true
spec:
# kcp only ever READS from the source.
source:
type: msk # msk | apache-kafka. Gates auth: iam is msk-only.
bootstrapServers:
- b-1.msk-prod.abc123.c2.kafka.us-east-1.amazonaws.com:9096
- b-2.msk-prod.abc123.c2.kafka.us-east-1.amazonaws.com:9096
# Either an inline block (below) or a path to a credentials file:
# credentials: /etc/kcp/source-creds.yaml
# The inline mapping is the same shape as that file's top level, so the two
# spellings are a copy-paste apart. Prefer the path form for a Kubernetes
# Secret mounted as a file.
credentials:
sasl_scram:
username: ${MSK_USERNAME}
password: ${MSK_PASSWORD}
mechanism: SHA512 # SHA256 | SHA512 — MSK is SHA512-only
target:
type: confluent-cloud # confluent-cloud | confluent-platform
clusterId: lkc-abc123 # required for BOTH destination types
kafka:
bootstrapServers:
- pkc-xxxxx.us-east-1.aws.confluent.cloud:9092
restEndpoint: https://pkc-xxxxx.us-east-1.aws.confluent.cloud:443
# The destination KAFKA leg. Only sasl_plain is supported in this release
# — the destination client is hardcoded to SASL/PLAIN over TLS, so any
# other block would be accepted and then silently ignored.
credentials:
sasl_plain:
username: ${CC_API_KEY}
password: ${CC_API_SECRET}
tls: true
# insecure_skip_tls_verify is a SIBLING of the auth block, not a key
# inside it, and it reaches all three connection legs:
# insecure_skip_tls_verify: true
#
# restCredentials is OPTIONAL and DERIVED in full from credentials when
# omitted: api_key/api_secret from sasl_plain.username/password, and
# insecure_skip_verify from insecure_skip_tls_verify above.
#
# Spell it out only for a REST endpoint behind a private CA. A block that
# is present is used exactly as written — never partially derived — so it
# must restate the key and secret.
#
# restCredentials:
# api_key: ${CC_API_KEY}
# api_secret: ${CC_API_SECRET}
# ca_cert: /etc/kcp/dest-ca.pem
clusterLink:
name: msk-to-cc # the link must ALREADY EXIST on the destination
# Disable the link's consumer.offset.sync.enable during apply and restore it
# after switchover. Requires the link to currently have it enabled.
pauseConsumerOffsetSync: true
gateway:
namespace: confluent
# kubeconfig: ~/.kube/config # optional; a leading ~/ IS expanded
crs:
initial: gateway-initial # a Kubernetes object NAME, read live
switchover: /etc/kcp/gateway-switchover.yaml # a local FILE path
# Route(s) to fence at cutover, by spec.routes[].name. kcp reads the live
# initial CR, injects fence: {scope: ALL, errorCode: BROKER_NOT_AVAILABLE}
# onto each named route, and applies the patched CR — there is no separate
# fenced-CR file to create or keep in sync. The named routes must exist in
# the initial CR and must not already be fenced.
fence:
routes:
- migration-route
# OPTIONAL. A flat list of LITERAL topic names, exact-matched against the
# link's active mirror topics — not globs, and a name that matches nothing is
# a hard error. Omit the key entirely to cut over every active mirror topic;
# an empty list is rejected rather than silently meaning "all".
#
# Note this has NO effect on `lag-check`, which always watches every mirror.
topics: ['t1.order', 't1.inventory', 't2.order']
# OPTIONAL, and read FRESH on every execute — never frozen at init. That is what
# lets policy be varied between init and execute. Each value is a DEFAULT:
# `kcp migration execute` can override any of them for a single run with the
# matching flag (e.g. --detect-unrouted-producers-duration 60s), no edit needed.
defaultPolicies:
lagThreshold: 0
promoteBatchSize: 100 # 0 = promote all at once
rolloutTimeout: 10m # 0 = no deadline
detectUnroutedProducersDuration: 30s # 0 = CHECK SKIPPED; min 10s when set
consumerOffsetSyncDrainDuration: 15s # 0 = no wait; needs pauseConsumerOffsetSync