@@ -354,7 +354,8 @@ spec:
354
354
renewBefore : " 720h" # 30 days
355
355
` ` `
356
356
357
- The function examines the watched Ingress and dynamically requests related resources:
357
+ The function examines the watched Ingress and dynamically requests related
358
+ resources:
358
359
359
360
` ` ` python
360
361
from crossplane.function import request, response
@@ -363,38 +364,70 @@ def operate(req, rsp):
363
364
# Access the watched Ingress resource
364
365
ingress = request.get_required_resource(req, "ops.crossplane.io/watched-resource")
365
366
if not ingress :
366
- response.set_output (rsp, {"error" : " No watched resource found" } )
367
+ response.fatal (rsp, " No watched resource found")
367
368
return
368
369
369
370
# Extract the service name from the Ingress backend
370
371
rules = ingress.get("spec", {}).get("rules", [])
371
372
if not rules :
373
+ response.fatal(rsp, "Could not extract service name from ingress")
372
374
return
373
375
374
376
backend = rules[0].get("http", {}).get("paths", [{}])[0].get("backend", {})
375
377
service_name = backend.get("service", {}).get("name")
376
378
if not service_name :
379
+ response.fatal(rsp, "Could not extract service name from ingress")
377
380
return
378
381
379
382
ingress_namespace = ingress.get("metadata", {}).get("namespace", "default")
380
383
381
- # Always declare what resources we need (requirements must be stable across calls)
382
- rsp.requirements.resources["related-service"].api_version = "v1"
383
- rsp.requirements.resources["related-service"].kind = "Service"
384
- rsp.requirements.resources["related-service"].match_name = service_name
385
- rsp.requirements.resources["related-service"].namespace = ingress_namespace
384
+ # CRITICAL: Always request the same resources to ensure requirement
385
+ # stabilization. Crossplane calls the function repeatedly until
386
+ # requirements don't change.
387
+ response.require_resources(
388
+ rsp,
389
+ name="related-service",
390
+ api_version="v1",
391
+ kind="Service",
392
+ match_name=service_name,
393
+ namespace=ingress_namespace
394
+ )
386
395
387
- # Process the service if Crossplane fetched it after our previous response
396
+ # Check if the service is available and process accordingly
388
397
service = request.get_required_resource(req, "related-service")
389
398
if service :
390
- create_certificate_for_service(ingress, service, rsp)
399
+ # Success: Both resources available
400
+ response.set_output(rsp, {
401
+ " status " : " success" ,
402
+ " message " : " Certificate management completed" ,
403
+ " ingress_host " : ingress.get("spec", {}).get("rules", [{}])[0].get("host"),
404
+ " service_name " : service.get("metadata", {}).get("name")
405
+ })
406
+ return
407
+
408
+ # Waiting: Service not available yet
409
+ response.set_output(rsp, {
410
+ " status " : " waiting" ,
411
+ " message " : f"Waiting for service '{service_name}' to be available"
412
+ })
391
413
```
392
414
415
+ {{<hint "important">}}
416
+ ** Critical resource stabilization pattern** : functions must return the ** same
417
+ requirements** in each iteration to signal completion. The function in the
418
+ preceding example always calls ` response.require_resources() ` regardless of
419
+ whether the service exists. This ensures Crossplane knows when to stop calling
420
+ the function.
421
+
422
+ Common mistake: only requesting resources when missing breaks the stabilization
423
+ contract and causes timeout errors.
424
+ {{</hint >}}
425
+
393
426
This pattern allows functions to:
394
- 1. Examine the watched resource
395
- 2. Dynamically determine what other resources you need
396
- 3. Request those resources in the next response
397
- 4. Process the more resources in next calls
427
+ 1 . Examine the watched resource (injected automatically)
428
+ 2 . Dynamically determine what other resources the function needs
429
+ 3 . Request those resources consistently using ` response.require_resources() `
430
+ 4 . Process all resources when available, or provide status when waiting
398
431
399
432
## Status and monitoring
400
433
0 commit comments