Welcome to Chapter 6! In the previous chapter, InitContainer Hooks Implementation, we learned how Velero performs "surgery" on your Pods to inject setup tasks that run before your application starts.
But what if you need to do something after the application is up and running?
Imagine you are moving into a new house (Performing a Restore).
Exec Hooks are like setting up that Smart TV. They are commands that run inside your container only after the container is alive and running.
Imagine you restored a database. The database software starts up, but it is in "Safe Mode" or "Read-Only Mode" because it detects it was restored from a backup.
mongo_unfreeze.sh) inside the running container.Unlike InitContainers, which modify the text definition of the Pod, Exec Hooks work by "remote controlling" the Pod.
Velero cannot run a command in a container that doesn't exist yet. When you restore a Pod, Velero submits the request to Kubernetes and then pauses. It acts like a waiter, watching the Pod until it sees the status change to Running.
Once the Pod is running, Velero uses the Kubernetes API (just like when you type kubectl exec in your terminal) to send a command to the specific container.
What if the application crashes immediately or hangs? Velero has a stopwatch (Timeout). If the Pod doesn't become ready, or if the command takes too long, Velero stops waiting to prevent the entire restore job from freezing forever.
Let's say we have a Pod named my-database. We want to run echo "Database Ready" inside it after restore.
(As learned in Configuration via Pod Annotations), we have this annotation on our Pod:
post.hook.restore.velero.io/command: '["/bin/sh", "-c", "echo Database Ready"]'
post.hook.restore.velero.io/wait-timeout: "2m"
my-database.
The command runs inside the container. If you were watching the logs, you would see the command execute. If it fails (returns an exit code other than 0), Velero marks the hook as failed (depending on your onError setting).
How does Velero manage to "jump" into a running container? It doesn't actually jump; it asks the Kubernetes API Server to run the process on its behalf.
mysql, all hooks for sidecar).
Before executing, Velero must ensure the Pod is ready. This logic is found in pkg/restore/restore_action.go (conceptually).
// Simplified logic: Waiting for the Pod to start
func waitForPod(k8sClient Client, podName string) error {
// We loop periodically to check the status
return wait.Poll(time.Second, time.Minute, func() (bool, error) {
pod := k8sClient.GetPod(podName)
// We only proceed if the Phase is 'Running'
if pod.Status.Phase == corev1.PodRunning {
return true, nil
}
return false, nil // Keep waiting
})
}
Explanation: This code uses a simple loop (wait.Poll). It fetches the Pod's status repeatedly. If the status is Running, it returns true (stop waiting). If not, it sleeps for a second and tries again, up to a limit (1 minute in this snippet).
Once the Pod is ready, Velero prepares the execution request. This uses the client-go library to talk to Kubernetes.
// Simplified logic: preparing the "Remote Control" signal
func executeCommand(client K8sClient, podName, container, cmd) {
// Create the request to the API Server
req := client.RESTClient().Post().
Resource("pods").
Name(podName).
SubResource("exec").
Param("container", container).
Param("command", cmd)
// "Stream" initiates the connection
executor := remotecommand.NewSPDYExecutor(config, "POST", req.URL())
}
Explanation:
/api/v1/.../pods/my-pod/exec.container to use and what command to run.NewSPDYExecutor is the technical term for opening a two-way connection (stream) so Velero can send the command and receive the output (stdout/stderr).Finally, Velero triggers the command and captures the output.
// Simplified logic: Pushing the button
func runStream(executor Executor) error {
// Connect standard output (stdout) and error (stderr)
var stdout, stderr bytes.Buffer
// Execute!
err := executor.Stream(StreamOptions{
Stdout: &stdout,
Stderr: &stderr,
})
// If err is not nil, the command failed (non-zero exit code)
return err
}
Explanation: This function actually runs the command. It captures any text the command prints (stdout) so it can be logged in the Velero restore logs. If the command fails (e.g., file not found), err will contain the details.
In this chapter, we learned:
Running.We have now covered the entire lifecycle of hooks! From configuration (CRDs and Annotations) to validation, and finally to implementation (InitContainers and Exec).
In the final chapter, we will bring everything together with a concrete code walkthrough on how Velero parses the specific annotations we discussed earlier.
Next Chapter: Code Example - Parsing Annotations
Generated by Code IQ