Lab 3.1: Exploring Controller Runtime
Related Lesson: Lesson 3.1: Controller Runtime Deep Dive
Navigation: Module Overview | Next Lab: Designing API →
Objectives
- Explore controller-runtime architecture
- Understand Manager setup
- Implement different requeue scenarios
- Trace reconciliation calls
Prerequisites
- Completion of Module 2
- Kind cluster running
- Understanding of basic operator structure
Exercise 1: Examine Manager Setup
Task 1.1: Review Your Hello World Operator
# Navigate to your hello-world-operator from Module 2
cd ~/hello-world-operator
# Examine main.go
cat main.go
Questions:
- How is the Manager created?
- What options are configured?
- How is the reconciler set up?
Task 1.2: Understand Manager Options
// In main.go, examine the Manager options:
mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
Scheme: scheme,
MetricsBindAddress: metricsAddr,
Port: 9443,
HealthProbeBindAddress: probeAddr,
LeaderElection: enableLeaderElection,
})
Questions:
- What does each option do?
- Why is leader election important?
- What’s the purpose of metrics and health probes?
Exercise 2: Explore Reconcile Function
Task 2.1: Examine Current Reconcile Function
# Look at your controller
cat internal/controller/helloworld_controller.go
Observe:
- Function signature
- How it reads resources
- How it returns results
- Error handling
Task 2.2: Add Logging
Add detailed logging to understand the flow:
func (r *HelloWorldReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
log := log.FromContext(ctx)
log.Info("Reconcile called", "request", req)
// ... existing code ...
log.Info("Reconcile completed", "result", result)
return result, err
}
Exercise 3: Implement Different Requeue Scenarios
Task 3.1: Immediate Requeue
Modify your controller to requeue immediately on certain conditions:
// If ConfigMap is being created, requeue to check status
if !configMapCreated {
log.Info("ConfigMap not ready, requeuing")
return ctrl.Result{Requeue: true}, nil
}
Task 3.2: Delayed Requeue
Add a delayed requeue for rate limiting:
// If external dependency is not ready, check again in 10 seconds
if !dependencyReady {
log.Info("Dependency not ready, requeuing in 10s")
return ctrl.Result{RequeueAfter: 10 * time.Second}, nil
}
Task 3.3: No Requeue
Ensure success cases don’t requeue:
// Everything is in desired state
log.Info("Reconciliation successful")
return ctrl.Result{}, nil
Exercise 4: Trace Reconciliation Calls
Task 4.1: Run Operator with Verbose Logging
# Run operator
make run
# In another terminal, create a resource
kubectl apply -f - <<EOF
apiVersion: hello.example.com/v1
kind: HelloWorld
metadata:
name: trace-test
spec:
message: "Trace me"
count: 3
EOF
Observe:
- When Reconcile is called
- What request is passed
- What result is returned
- How often it’s called
Task 4.2: Modify Resource and Observe
# Update the resource
kubectl patch helloworld trace-test --type merge -p '{"spec":{"count":5}}'
# Watch logs - see reconciliation triggered
Exercise 5: Understand Client Usage
Task 5.1: Examine Client Operations
In your controller, identify:
r.Get()callsr.Create()callsr.Update()callsr.Status().Update()calls
Task 5.2: Add Client Error Handling
Improve error handling:
// Get resource with proper error handling
if err := r.Get(ctx, req.NamespacedName, helloWorld); err != nil {
if errors.IsNotFound(err) {
log.Info("Resource not found, may have been deleted")
return ctrl.Result{}, nil
}
log.Error(err, "Failed to get resource")
return ctrl.Result{}, err
}
Exercise 6: Test Different Scenarios
Task 6.1: Test Resource Deletion
# Delete resource
kubectl delete helloworld trace-test
# Observe logs - see reconciliation on deletion
Task 6.2: Test Concurrent Updates
# Create resource
kubectl apply -f resource.yaml
# Quickly update multiple times
kubectl patch helloworld test --type merge -p '{"spec":{"count":1}}'
kubectl patch helloworld test --type merge -p '{"spec":{"count":2}}'
kubectl patch helloworld test --type merge -p '{"spec":{"count":3}}'
# Observe how reconciliation handles rapid changes
Cleanup
# Delete test resources
kubectl delete helloworld trace-test test 2>/dev/null || true
Lab Summary
In this lab, you:
- Explored Manager setup and configuration
- Understood Reconcile function flow
- Implemented different requeue strategies
- Traced reconciliation calls
- Improved error handling
Key Learnings
- Manager coordinates all controller components
- Reconcile function is called for each resource change
- Different requeue strategies for different scenarios
- Client provides type-safe access to resources
- Proper error handling is crucial
- Logging helps understand reconciliation flow
Next Steps
Now that you understand controller-runtime, let’s design a proper API for a database operator!
| Navigation: ← Module Overview | Related Lesson | Next Lab: Designing API → |