# Hook Sidecar Container
## Introduction
In KubeVirt, a Hook Sidecar container is a sidecar container (a secondary container that runs along with the main
application container within the same Pod) used to apply customizations before the Virtual Machine is initialized. This
ability is provided since configurable elements in the VMI specification do not cover all of the [libvirt domain
XML](https://libvirt.org/formatdomain.html) elements.
The sidecar containers communicate with the main container over a socket with a gRPC protocol. There are two main
sidecar hooks:
1. `onDefineDomain`: This hook helps to customize libvirt's XML and return the new XML over gRPC for the VM creation.
2. `preCloudInitIso`: This hook helps to customize the cloud-init configuration. It operates on and returns JSON
formatted cloud-init data.
## Enabling `Sidecar` feature gate
`Sidecar` feature gate can be enabled by following the steps mentioned in
[Activating feature gates](../cluster_admin/activating_feature_gates.md).
In case of a development cluster created using kubevirtci, follow the steps mentioned in the
[developer doc](https://github.com/kubevirt/kubevirt/blob/main/docs/getting-started.md#compile-and-run-it) to enable
the feature gate.
## Sidecar-shim container image
To run a VM with custom modifications, the [sidecar-shim-image](https://quay.io/repository/kubevirt/sidecar-shim)
takes care of implementing the communication with the main container.
The image contains the `sidecar-shim` binary built using
[`sidecar_shim.go`](https://github.com/kubevirt/kubevirt/blob/main/cmd/sidecars/sidecar_shim.go) which should be kept
as the entrypoint of the container. This binary will search in `$PATH` for binaries named after the hook names (e.g
`onDefineDomain` and `preCloudInitIso`) and run them. Users must provide the necessary arguments as command line options
(flags).
In the case of `onDefineDomain`, the arguments will be the VMI information as JSON string, (e.g `--vmi vmiJSON`) and
the current domain XML (e.g `--domain domainXML`). It outputs the modified domain XML on the standard output.
In the case of `preCloudInitIso`, the arguments will be the VMI information as JSON string, (e.g `--vmi vmiJSON`) and
the CloudInitData (e.g `--cloud-init cloudInitJSON`). It outputs the modified CloudInitData (as JSON) on the standard ouput.
Shell or python scripts can be used as alternatives to the binary, by making them available at the expected location
(`/usr/bin/onDefineDomain` or `/usr/bin/preCloudInitIso` depending upon the hook).
A prebuilt image named `sidecar-shim` capable of running Shell or Python scripts is shipped as part of KubeVirt releases.
## Go, Python, Shell - pick any one
Although a binary doesn't strictly need to be generated from Go code, and a script doesn't strictly need to be one
among Shell or Python, for the purpose of this guide, we will use those as examples.
### Go binary
Example Go code modifiying the [SMBIOS system
information](https://libvirt.org/formatdomain.html#smbios-system-information) can be found in the [KubeVirt
repo](https://github.com/kubevirt/kubevirt/tree/main/cmd/sidecars/smbios). Binary generated from this code, when
available under `/usr/bin/ondefinedomain` in the sidecar-shim-image, is run right before VMI creation and the baseboard
manufacturer value is modified to reflect what's provided in the `smbios.vm.kubevirt.io/baseBoardManufacturer`
annotation in [VMI spec](https://github.com/kubevirt/kubevirt/blob/main/examples/vmi-with-sidecar-hook.yaml).
### Shell or Python script
If you pefer writing a shell or python script instead of a Go program, create a Kubernetes ConfigMap and use
annotations to make sure the script is run before the VMI creation. The flow would be as below:
1. Create a ConfigMap containing the shell or python script you want to run
1. Create a VMI containing the annotation `hooks.kubevirt.io/hookSidecars` and mention the ConfigMap information in it.
1. In this case a predefined image can be used to handle the communication with the main container.
#### ConfigMap with shell script
```yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: my-config-map
data:
my_script.sh: |
#!/bin/sh
tempFile=`mktemp --dry-run`
echo $4 > $tempFile
sed -i "s||Radical Edward|" $tempFile
cat $tempFile
```
#### ConfigMap with python script
```yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: my-config-map
data:
my_script.sh: |
#!/usr/bin/env python3
import xml.etree.ElementTree as ET
import sys
def main(s):
# write to a temporary file
f = open("/tmp/orig.xml", "w")
f.write(s)
f.close()
# parse xml from file
xml = ET.parse("/tmp/orig.xml")
# get the root element
root = xml.getroot()
# find the baseBoard element
baseBoard = root.find("sysinfo").find("baseBoard")
# prepare new element to be inserted into the xml definition
element = ET.Element("entry", {"name": "manufacturer"})
element.text = "Radical Edward"
# insert the element
baseBoard.insert(0, element)
# write to a new file
xml.write("/tmp/new.xml")
# print file contents to stdout
f = open("/tmp/new.xml")
print(f.read())
f.close()
if __name__ == "__main__":
main(sys.argv[4])
```
After creating one of the above ConfigMap, create the VMI using the manifest in [this
example](https://github.com/kubevirt/kubevirt/blob/main/examples/vmi-with-sidecar-hook-configmap.yaml). Of importance
here is the ConfigMap information stored in the annotations:
```yaml
annotations:
hooks.kubevirt.io/hookSidecars: >
[
{
"args": ["--version", "v1alpha2"],
"configMap": {"name": "my-config-map", "key": "my_script.sh", "hookPath": "/usr/bin/onDefineDomain"}
}
]
```
The `name` field indicates the name of the ConfigMap on the cluster which contains the script you want to execute. The
`key` field indicates the key in the ConfigMap which contains the script to be executed. Finally, `hookPath` indicates
the path where you want the script to be mounted. It could be either of `/usr/bin/onDefineDomain` or
`/usr/bin/preCloudInitIso` depending upon the hook you want to execute.
An optional value can be specified with the `"image"` key if a custom image is needed, if omitted the default Sidecar-shim image built together with the other KubeVirt images will be used.
The default Sidecar-shim image, if not override with a custom value, will also be updated as other images as for [Updating KubeVirt Workloads](../cluster_admin/updating_and_deletion/#updating-kubevirt-workloads).
### Verify everything works
Whether you used the Go binary or a Shell/Python script from the above examples, you would be able to see the newly
created VMI have the modified baseboard manufacturer information. After creating the VMI, verify that it is in the
`Running` state, and connect to its console and see if the desired changes to baseboard manufacturer get reflected:
```shell
# Once the VM is ready, connect to its display and login using name and password "fedora"
cluster/virtctl.sh vnc vmi-with-sidecar-hook-configmap
# Check whether the base board manufacturer value was successfully overwritten
sudo dmidecode -s baseboard-manufacturer
```