Subscribe functions to CloudEvents¶
Configure which Broker events should reach a function, then deploy that configuration as Knative Triggers. func subscribe writes subscription metadata to the local func.yaml; it does not create a Trigger on the cluster by itself.
Prerequisites¶
- You have installed the CLI and opened the initialized function project directory.
- The handler accepts CloudEvents; use an appropriate CloudEvents template or implement the corresponding handler contract.
- The handler satisfies the event response contract, not merely accepts an event POST. Return a successful HTTP response with an empty body—for example,
204 No Content—or a valid reply CloudEvent with a successful status. A generic HTTP greeting body is not automatically a valid event-sink response. The MTChannelBasedBroker v1.24 response handler rejects non-empty responses that are not recognized as CloudEvents and rejects malformed reply events. - Knative Serving and Eventing are installed on the intended cluster. You can deploy the function and create Triggers in the intended namespace.
- A Broker exists and is Ready in that namespace. The name
defaultis a convention, not a guarantee that a Broker exists. - You know the attributes of the events the producer sends to that Broker, and can send a matching test event using the producer's documented procedure.
- You meet the deployment prerequisites, including registry access when building/pushing.
Procedure¶
From the function project directory, use subscribe to save Broker subscriptions and exact-match CloudEvents attribute filters in func.yaml. Deploy afterward to create the corresponding Triggers. Here, --source is the Broker name, not the event's CloudEvents source attribute.
Choose one Broker for this procedure. Replace my-broker and the example filter values with your Broker and producer's event attributes. To use a Ready Broker named default, substitute --source default in the same command rather than running another subscription command. Both example filters must match. Functions from Knative v1.24.0 rejects malformed --filter entries (missing = or an empty key); values may contain =. Repeating subscribe for the same Broker merges filter keys into that local subscription, replacing values for matching keys; it does not clear other keys. Inspect the saved configuration before deploying.
To subscribe the function to events for a given broker, run the following command:
func subscribe --filter type=com.example --filter extension=my-extension-value --source my-broker
To subscribe the function to events for a given broker, run the following command:
kn func subscribe --filter type=com.example --filter extension=my-extension-value --source my-broker
Deployment with Triggers¶
Inspect deploy.subscriptions in func.yaml to confirm the Broker and filters, then deploy using the deployment guide. Only configured subscriptions result in Triggers; the CLI's "Creating Triggers" message alone is not proof that a subscription exists.
Before any deployment, select the intended cluster and the Broker's namespace in this shell:
func uses the effective kubeconfig and its current context. Running a kubectl or kn check with --context does not select that context for a later func command. Neither --remote=false nor invoke --target=remote selects a cluster.
In every shell used for cluster-facing Functions commands, select a trusted kubeconfig file and set the expected context and namespace. Replace all three example values with your intended environment; use a single kubeconfig file for this procedure, not a merged file list:
export KUBECONFIG=/absolute/path/to/intended-kubeconfig
EXPECTED_CONTEXT=your-intended-context
NAMESPACE=your-namespace
check_func_context() {
if [ -z "$EXPECTED_CONTEXT" ] || [ -z "$NAMESPACE" ]; then
printf '%s\n' 'STOP: set the intended context and namespace.' >&2
return 1
fi
if [ ! -f "$KUBECONFIG" ]; then
printf '%s\n' 'STOP: intended kubeconfig file is missing.' >&2
return 1
fi
actual_context=$(kubectl config current-context) || return 1
if [ "$actual_context" != "$EXPECTED_CONTEXT" ]; then
printf '%s\n' 'STOP: kubeconfig current context does not match the intended context.' >&2
return 1
fi
}
check_func_context
Stop on failure. Do not deploy or invoke remotely until the effective current context matches the intended cluster. Confirm that the selected file/context belongs to that cluster using information from your operator or provider; see CLI connection guidance. Do not change the user's default kubeconfig to make a check pass.
Keep KUBECONFIG set and use check_func_context && before each cluster-facing func or kn func command, as below. If you open another terminal, repeat this setup there. The guard checks context selection, not resource ownership or authorization.
Existing generated-name Triggers, including first deployment
In Functions from Knative v1.24.0, the Knative deployer ignores an already-existing generated Trigger rather than updating it. This can hide a first-deployment name collision, not just stale filters after redeployment. Inspect any existing <function-name>-function-trigger-<index> resources before deploying; the index corresponds to the entry in deploy.subscriptions.
Compare the Trigger owner reference's apiVersion, kind, name, and UID with the live Knative Service you are authorized to manage (serving.knative.dev/v1, Service, its name, and its current metadata.uid). Also compare spec.subscriber.ref, spec.broker, and spec.filter.attributes with that Service and the intended subscription. A matching generated-looking name is not evidence of ownership.
Stop if the owner is absent, foreign, or refers to an older Service UID, or if any intended routing/filter setting differs. If no function Service exists yet, an existing generated-name Trigger cannot be assumed to belong to the Service you are about to create. Consult the resource owner and the Eventing Trigger guide for an authorized resolution; do not delete shared resources or assume redeployment updates them.
For read-only preflight inspection, replace hello with your project name and use the guarded context/namespace:
FUNCTION_NAME=hello
check_func_context && kubectl --context "$EXPECTED_CONTEXT" get kservice "$FUNCTION_NAME" --namespace "$NAMESPACE" -o yaml
check_func_context && kubectl --context "$EXPECTED_CONTEXT" get triggers --namespace "$NAMESPACE"
For a new Service, NotFound is expected for the Service check; still inspect any colliding Trigger names. Authentication/permission failures are not evidence that no resources exist—stop and ask the operator. Inspect relevant Trigger YAML as shown under verification before deciding it is safe to proceed.
If the project's registry is already configured and these checks pass, deploy to the Broker's namespace:
Deploy the function with Triggers by running the command inside the project directory:
check_func_context && func deploy --remote=false --deployer=knative --namespace "$NAMESPACE"
Deploy the function with Triggers by running the command inside the project directory:
check_func_context && kn func deploy --remote=false --deployer=knative --namespace "$NAMESPACE"
Example output
🙌 Function image built: <registry>/hello:latest
🎯 Creating Triggers on the cluster
✅ Function deployed in namespace "default" and exposed at URL:
http://hello.default.127.0.0.1.sslip.io
Your image, namespace, and URL will differ. Use your deployment's values for verification.
Verify event delivery¶
Keep the same guarded kubeconfig/current context, FUNCTION_NAME, and Broker namespace used for deployment. Use read-only checks:
check_func_context && kubectl --context "$EXPECTED_CONTEXT" get broker --namespace "$NAMESPACE"
check_func_context && kubectl --context "$EXPECTED_CONTEXT" get kservice "$FUNCTION_NAME" --namespace "$NAMESPACE" -o yaml
check_func_context && kubectl --context "$EXPECTED_CONTEXT" get triggers --namespace "$NAMESPACE"
Repeat the full owner apiVersion/kind/name/UID and subscriber/Broker/filter comparisons from preflight, using the live Service's current UID. Stop on a foreign/stale owner or configuration mismatch; an existing name does not make a Trigger automatically safe to update. For Functions from Knative v1.24.0, the first generated name is typically hello-function-trigger-0; use the actual name in your namespace:
check_func_context && kubectl --context "$EXPECTED_CONTEXT" get trigger hello-function-trigger-0 --namespace "$NAMESPACE" -o yaml
check_func_context && kubectl --context "$EXPECTED_CONTEXT" describe trigger hello-function-trigger-0 --namespace "$NAMESPACE"
Check the Broker, subscriber, filters, and Ready condition. Then verify a fresh, correlated delivery, not just any earlier handler response:
- Prepare a new test event with a fresh CloudEvents
idand record its send time. If your handler does not expose the ID, use a unique payload correlation marker supported by the application and record it instead. - Send that event to the Broker through your producer's documented path, with all filter attributes matching. For the example filters above, both
type=com.exampleandextension=my-extension-valuemust match. - Inspect the function's logs or application result in the corresponding test time window. Confirm the same fresh event ID or correlation marker from this Broker test; old log entries, a generic "received" message, or an earlier direct
func invokeresponse do not establish this delivery.
Correlating a fresh event ID establishes receipt, not successful acknowledgment. Also verify the handler's response and check the Broker's delivery diagnostics for response-contract failures during the test window. Record repeated receipts separately: successful acknowledgment does not prove that no retries occurred, and this check does not establish exactly-once delivery. A fresh receipt accompanied by an invalid-response error is not a successful delivery check.
If your available output cannot identify the fresh event, report the delivery check as inconclusive, not successful. Consult the application owner for a supported correlation/observability method rather than inventing a logging guarantee.
func invoke --target=remote bypasses the Broker; it can isolate handler/endpoint problems but cannot verify this subscription. If the handler works directly but events do not arrive, follow Events are not delivered. Stop at permission or ownership barriers and pass the condition output to the responsible operator; do not infer that every missing event is a handler failure.