Skip to content

Commit

Permalink
Add virt-xml sidecar hook
Browse files Browse the repository at this point in the history
The virt-xml tools is a command line utility for editing libvirt XML.

This setup introduces a new hook to integrate virt-xml in KubeVirt.
Using virt-xml allows to use a well-maintained tool to try and test unsupported
options by the KubeVirt APIs.

This feature is supposed to help developers to try and test unsupported
options without the need to develop their own sidecar.

Signed-off-by: Alice Frosi <afrosi@redhat.com>
  • Loading branch information
alicefr committed Dec 20, 2021
1 parent a60d448 commit 78a99f3
Show file tree
Hide file tree
Showing 10 changed files with 1,134 additions and 820 deletions.
9 changes: 9 additions & 0 deletions BUILD.bazel
Expand Up @@ -291,6 +291,15 @@ container_push(
tag = "$(container_tag)",
)

container_push(
name = "push-virt-xml-hook",
format = "Docker",
image = "//cmd/virt-xml-hook:virt-xml-hook-image",
registry = "$(container_prefix)",
repository = "$(image_prefix)virt-xml-hook",
tag = "$(container_tag)",
)

genrule(
name = "build-virtctl",
srcs = [
Expand Down
1,008 changes: 456 additions & 552 deletions WORKSPACE

Large diffs are not rendered by default.

49 changes: 49 additions & 0 deletions cmd/virt-xml-hook/BUILD.bazel
@@ -0,0 +1,49 @@
load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library")
load(
"@io_bazel_rules_docker//container:container.bzl",
"container_image",
)

container_image(
name = "version-container",
tars = select({
"//conditions:default": [
"//rpm:hookxmlbase_x86_64",
],
}),
)

container_image(
name = "virt-xml-hook-image",
architecture = select({
"@io_bazel_rules_go//go/platform:linux_arm64": "arm64",
"//conditions:default": "amd64",
}),
base = ":version-container",
directory = "/",
entrypoint = ["/virt-xml-hook"],
files = [":virt-xml-hook"],
visibility = ["//visibility:public"],
)

go_library(
name = "go_default_library",
srcs = ["virt_xml_hook.go"],
importpath = "kubevirt.io/kubevirt/cmd/virt-xml-hook",
visibility = ["//visibility:private"],
deps = [
"//pkg/hooks:go_default_library",
"//pkg/hooks/info:go_default_library",
"//pkg/hooks/v1alpha1:go_default_library",
"//pkg/hooks/v1alpha2:go_default_library",
"//staging/src/kubevirt.io/client-go/log:go_default_library",
"//vendor/github.com/spf13/pflag:go_default_library",
"@org_golang_google_grpc//:go_default_library",
],
)

go_binary(
name = "virt-xml-hook",
embed = [":go_default_library"],
visibility = ["//visibility:public"],
)
139 changes: 139 additions & 0 deletions cmd/virt-xml-hook/virt_xml_hook.go
@@ -0,0 +1,139 @@
package main

import (
"bytes"
"context"
"fmt"
"net"
"os"
"os/exec"
"strings"

"github.com/spf13/pflag"
"google.golang.org/grpc"

"kubevirt.io/client-go/log"
"kubevirt.io/kubevirt/pkg/hooks"
hooksInfo "kubevirt.io/kubevirt/pkg/hooks/info"
hooksV1alpha1 "kubevirt.io/kubevirt/pkg/hooks/v1alpha1"
hooksV1alpha2 "kubevirt.io/kubevirt/pkg/hooks/v1alpha2"
)

const (
onDefineDomainLoggingMessage = "Hook's OnDefineDomain callback method has been called"
usage = `updater
--version v1alpha1|v1alpha2
--args ''`
)

type infoServer struct {
Version string
}

func (s infoServer) Info(ctx context.Context, params *hooksInfo.InfoParams) (*hooksInfo.InfoResult, error) {
log.Log.Info("Hook's Info method has been called")

return &hooksInfo.InfoResult{
Name: "update-xml",
Versions: []string{
s.Version,
},
HookPoints: []*hooksInfo.HookPoint{
{
Name: hooksInfo.OnDefineDomainHookPointName,
Priority: 0,
},
},
}, nil
}

const virtXML = "virt-xml"

type v1alpha1Server struct {
args []string
}

type v1alpha2Server struct {
args []string
}

func MergeKubeVirtXMLWithProvidedXML(domainXML []byte, args []string) ([]byte, error) {
args = append(args, "--edit")
args = append(args, "--print-xml")
cmd := exec.Command(virtXML, args...)
cmd.Stdin = strings.NewReader(string(domainXML))
var stderr bytes.Buffer
cmd.Stderr = &stderr
log.Log.Infof("Execute command: %s", cmd.String())
out, err := cmd.Output()
if err != nil {
log.Log.Errorf("Fail running command stdout:%s stderr: %s error:%v", out, stderr.String(), err)
return []byte{}, err
}

return []byte(out), nil
}

func (s v1alpha2Server) OnDefineDomain(ctx context.Context, params *hooksV1alpha2.OnDefineDomainParams) (*hooksV1alpha2.OnDefineDomainResult, error) {
log.Log.Info(onDefineDomainLoggingMessage)
newDomainXML, err := MergeKubeVirtXMLWithProvidedXML(params.GetDomainXML(), s.args)
if err != nil {
return nil, err
}
return &hooksV1alpha2.OnDefineDomainResult{
DomainXML: newDomainXML,
}, nil
}
func (s v1alpha2Server) PreCloudInitIso(_ context.Context, params *hooksV1alpha2.PreCloudInitIsoParams) (*hooksV1alpha2.PreCloudInitIsoResult, error) {
return &hooksV1alpha2.PreCloudInitIsoResult{
CloudInitData: params.GetCloudInitData(),
}, nil
}

func (s v1alpha1Server) OnDefineDomain(ctx context.Context, params *hooksV1alpha1.OnDefineDomainParams) (*hooksV1alpha1.OnDefineDomainResult, error) {
log.Log.Info(onDefineDomainLoggingMessage)
newDomainXML, err := MergeKubeVirtXMLWithProvidedXML(params.GetVmi(), s.args)
if err != nil {
return nil, err
}
return &hooksV1alpha1.OnDefineDomainResult{
DomainXML: newDomainXML,
}, nil
}

func main() {
log.InitializeLogging("xml update")

var version string
pflag.StringVar(&version, "version", "", "hook version to use")

var options string
pflag.StringVar(&options, "args", "", "params to pass to virt-xml")
pflag.Parse()

args := strings.Split(options, "|")

socketPath := hooks.HookSocketsSharedDirectory + "/update.sock"
socket, err := net.Listen("unix", socketPath)
if err != nil {
log.Log.Reason(err).Errorf("Failed to initialized socket on path: %s", socket)
log.Log.Error("Check whether given directory exists and socket name is not already taken by other file")
panic(err)
}
defer os.Remove(socketPath)

server := grpc.NewServer([]grpc.ServerOption{}...)

if version == "" {
panic(fmt.Errorf(usage))
}
if options == "" {
panic(fmt.Errorf(usage))
}

hooksInfo.RegisterInfoServer(server, infoServer{Version: version})
hooksV1alpha1.RegisterCallbacksServer(server, v1alpha1Server{args: args})
hooksV1alpha2.RegisterCallbacksServer(server, v1alpha2Server{args: args})
log.Log.Infof("Starting hook server exposing 'info' and 'v1alpha1' services on socket %s", socketPath)
server.Serve(socket)
}
34 changes: 34 additions & 0 deletions examples/vmi-with-virt-xml-sidecar-hook.yaml
@@ -0,0 +1,34 @@
---
apiVersion: kubevirt.io/v1
kind: VirtualMachineInstance
metadata:
annotations:
hooks.kubevirt.io/hookSidecars: '[{"args": ["--version", "v1alpha2", "--args", "--disk=iotune.total_bytes_sec=52428800,iotune.total_iops_sec=1000|--edit=all"], "image":"registry:5000/kubevirt/virt-xml-hook:devel", "imagePullPolicy": "Always"}]'
labels:
special: vmi-with-virt-xml
name: vmi-with-virt-xml
spec:
domain:
devices:
disks:
- disk:
bus: virtio
name: containerdisk
- disk:
bus: virtio
name: cloudinitdisk
rng: {}
resources:
requests:
memory: 1024M
terminationGracePeriodSeconds: 0
volumes:
- containerDisk:
image: registry:5000/kubevirt/fedora-with-test-tooling-container-disk:devel
name: containerdisk
- cloudInitNoCloud:
userData: |-
#cloud-config
password: fedora
chpasswd: { expire: False }
name: cloudinitdisk
2 changes: 1 addition & 1 deletion hack/bazel-build-images.sh
Expand Up @@ -30,7 +30,7 @@ bazel build \
--define image_prefix= \
--define container_tag= \
//:build-other-images //cmd/virt-operator:virt-operator-image //cmd/virt-api:virt-api-image \
//cmd/virt-controller:virt-controller-image //cmd/virt-handler:virt-handler-image //cmd/virt-launcher:virt-launcher-image //cmd/libguestfs:libguestfs-tools-image //tests:conformance_image
//cmd/virt-controller:virt-controller-image //cmd/virt-handler:virt-handler-image //cmd/virt-launcher:virt-launcher-image //cmd/libguestfs:libguestfs-tools-image //cmd/virt-xml-hook //tests:conformance_image

rm -rf ${DIGESTS_DIR}
mkdir -p ${DIGESTS_DIR}
Expand Down
2 changes: 1 addition & 1 deletion hack/bazel-push-images.sh
Expand Up @@ -23,7 +23,7 @@ source hack/common.sh
source hack/bootstrap.sh
source hack/config.sh

PUSH_TARGETS=(${PUSH_TARGETS:-other-images virt-operator virt-api virt-controller virt-handler virt-launcher conformance libguestfs})
PUSH_TARGETS=(${PUSH_TARGETS:-other-images virt-operator virt-api virt-controller virt-handler virt-launcher conformance libguestfs virt-xml-hook})

for tag in ${docker_tag} ${docker_tag_alt}; do
for target in ${PUSH_TARGETS[@]}; do
Expand Down
2 changes: 1 addition & 1 deletion hack/bootstrap.sh
Expand Up @@ -26,7 +26,7 @@ KUBEVIRT_NO_BAZEL=${KUBEVIRT_NO_BAZEL:-false}
HOST_ARCHITECTURE="$(uname -m)"

sandbox_root=${SANDBOX_DIR}/default/root
sandbox_hash="b0e5d57b5529f4b066f9e8719cee63f6f7d5c418"
sandbox_hash="c3b12fc06fa2c519988f19abbd3d32b97fd3b07f"

function kubevirt::bootstrap::regenerate() {
(
Expand Down
13 changes: 13 additions & 0 deletions hack/rpm-deps.sh
Expand Up @@ -127,6 +127,10 @@ libguestfstools_x86_64="
edk2-ovmf-${EDK2_VERSION}
"

hookxml_base="
virt-install
"

if [ -z "${SINGLE_ARCH}" ] || [ "${SINGLE_ARCH}" == "x86_64" ]; then

bazel run \
Expand Down Expand Up @@ -207,6 +211,15 @@ if [ -z "${SINGLE_ARCH}" ] || [ "${SINGLE_ARCH}" == "x86_64" ]; then
--force-ignore-with-dependencies '^(man-db|mandoc)' \
--force-ignore-with-dependencies '^dbus'

# create a rpmtree for virt-handler
bazel run \
--config=${ARCHITECTURE} \
//:bazeldnf -- rpmtree --public --name hookxmlbase_x86_64 \
--basesystem centos-stream-release \
${bazeldnf_repos} \
$centos_base \
$hookxml_base

# remove all RPMs which are no longer referenced by a rpmtree
bazel run \
--config=${ARCHITECTURE} \
Expand Down

0 comments on commit 78a99f3

Please sign in to comment.