ci: integration tests

Signed-off-by: Guido Grazioli <ggraziol@redhat.com>
This commit is contained in:
Guido Grazioli
2023-07-27 14:50:08 +02:00
parent f5b8372a75
commit 198d885b8f
26 changed files with 626 additions and 703 deletions

View File

@@ -0,0 +1,62 @@
- name: Create VM
connection: local
gather_facts: false
hosts: localhost
tasks:
- name: Create a VirtualMachine
kubevirt.core.kubevirt_vm:
state: present
name: testvm
namespace: default
labels:
app: test
wait: yes
wait_timeout: 600
spec:
domain:
cpu:
sockets: 1
memory:
guest: 256Mi
devices:
interfaces:
- name: default
masquerade: {}
- name: bridge-network
bridge: {}
networks:
- name: default
pod: {}
- name: bridge-network
multus:
networkName: kindexgw
volumes:
- containerDisk:
image: quay.io/containerdisks/fedora:latest
name: containerdisk
- name: Create a 2nd VirtualMachine
kubevirt.core.kubevirt_vm:
state: present
name: testvm2
namespace: default
labels:
foo: bar
wait: yes
wait_timeout: 600
spec:
domain:
cpu:
sockets: 1
memory:
guest: 256Mi
devices:
interfaces:
- name: default
masquerade: {}
networks:
- name: default
pod: {}
volumes:
- containerDisk:
image: quay.io/containerdisks/fedora:latest
name: containerdisk

View File

@@ -1,59 +1,27 @@
#!/usr/bin/env bash
set -eux
set -o pipefail
source virtualenv.sh
pip install kubernetes PyYAML jsonpatch Jinja2
export ANSIBLE_ROLES_PATH="../"
./server.py &
USER_CREDENTIALS_DIR=$(pwd)
export USER_CREDENTIALS_DIR
cleanup() {
kill -9 "$(jobs -p)"
{
export ANSIBLE_CALLBACKS_ENABLED=profile_tasks
export ANSIBLE_INVENTORY_ENABLED=kubevirt.core.kubevirt,yaml
export ANSIBLE_PYTHON_INTERPRETER=auto_silent
ansible-inventory -i test.kubevirt.yml -y --list --output empty.yml "$@"
ansible-playbook playbook.yml "$@"
ansible-inventory -i test.kubevirt.yml -y --list --output all.yml "$@"
ansible-inventory -i test.label.kubevirt.yml -y --list --output label.yml "$@"
ansible-inventory -i test.net.kubevirt.yml -y --list --output net.yml "$@"
ansible-playbook verify.yml "$@"
} || {
exit 1
}
trap cleanup INT TERM EXIT
# Fake auth file
mkdir -p ~/.kube/
cat <<EOF > ~/.kube/config
apiVersion: v1
clusters:
- cluster:
insecure-skip-tls-verify: true
server: http://localhost:12345
name: development
contexts:
- context:
cluster: development
user: developer
name: dev-frontend
current-context: dev-frontend
kind: Config
preferences: {}
users:
- name: developer
user:
token: ZDNg7LzSlp8a0u0fht_tRnPMTOjxqgJGCyi_iy0ecUw
EOF
#################################################
# RUN THE PLUGIN
#################################################
# run the plugin second
export ANSIBLE_INVENTORY_ENABLED=kubevirt.core.kubevirt
cat << EOF > "$OUTPUT_DIR/test.kubevirt.yml"
plugin: kubevirt.core.kubevirt
connections:
- namespaces:
- default
EOF
ansible-inventory -vvvv -i "$OUTPUT_DIR/test.kubevirt.yml" --list --output="$OUTPUT_DIR/plugin.out"
#################################################
# DIFF THE RESULTS
#################################################
diff "$(pwd)/test.out" "$OUTPUT_DIR/plugin.out"

View File

@@ -1,322 +0,0 @@
#!/usr/bin/env python
from __future__ import absolute_import, division, print_function
__metaclass__ = type
import json
import os
from http import HTTPStatus
from http.server import HTTPServer
from http.server import SimpleHTTPRequestHandler
from threading import Thread
from urllib.parse import urlparse
class TestHandler(SimpleHTTPRequestHandler):
# Path handlers:
handlers = {}
def log_message(self, format, *args):
"""
Empty method, so we don't mix output of HTTP server with tests
"""
def do_GET(self):
params = urlparse(self.path)
if params.path in self.handlers:
self.handlers[params.path](self)
else:
SimpleHTTPRequestHandler.do_GET(self)
def do_POST(self):
params = urlparse(self.path)
if params.path in self.handlers:
self.handlers[params.path](self)
else:
self.send_error(HTTPStatus.NOT_FOUND)
class TestServer:
# The host and port and path used by the embedded tests web server:
PORT = None
# The embedded web server:
_httpd = None
# Thread for http server:
_thread = None
def set_json_response(self, path, code, body):
def _handle_request(handler):
handler.send_response(code)
handler.send_header("Content-Type", "application/json")
handler.end_headers()
data = json.dumps(body, ensure_ascii=False).encode("utf-8")
handler.wfile.write(data)
TestHandler.handlers[path] = _handle_request
def start_server(self, host="localhost"):
self._httpd = HTTPServer((host, 12345), TestHandler)
self._thread = Thread(target=self._httpd.serve_forever)
self._thread.start()
def stop_server(self):
self._httpd.shutdown()
self._thread.join()
if __name__ == "__main__":
print(os.getpid())
server = TestServer()
server.start_server()
server.set_json_response(path="/version", code=200, body={})
server.set_json_response(
path="/api",
code=200,
body={
"kind": "APIVersions",
"versions": ["v1"],
"serverAddressByClientCIDRs": [
{"clientCIDR": "0.0.0.0/0", "serverAddress": "localhost:12345"}
],
},
)
server.set_json_response(
path="/api/v1",
code=200,
body={
"kind": "APIResourceList",
"groupVersion": "v1",
"resources": [
{
"name": "services",
"singularName": "service",
"namespaced": True,
"kind": "Service",
"verbs": [
"create",
"delete",
"deletecollection",
"get",
"list",
"patch",
"update",
"watch",
],
"shortNames": ["svc"],
}
],
},
)
server.set_json_response(
path="/api/v1/namespaces/default/services",
code=200,
body={
"kind": "ServiceList",
"groupVersion": "v1",
"items": [],
},
)
server.set_json_response(
path="/apis",
code=200,
body={
"kind": "APIGroupList",
"apiVersion": "v1",
"groups": [
{
"name": "kubevirt.io",
"versions": [{"groupVersion": "kubevirt.io/v1", "version": "v1"}],
"preferredVersion": {
"groupVersion": "kubevirt.io/v1",
"version": "v1",
},
}
],
},
)
server.set_json_response(
path="/apis/kubevirt.io/v1",
code=200,
body={
"kind": "APIResourceList",
"apiVersion": "v1",
"groupVersion": "kubevirt.io/v1",
"resources": [
{
"name": "virtualmachineinstances",
"singularName": "virtualmachineinstance",
"namespaced": True,
"kind": "VirtualMachineInstance",
"verbs": [
"delete",
"deletecollection",
"get",
"list",
"patch",
"create",
"update",
"watch",
],
"shortNames": ["vmi", "vmis"],
}
],
},
)
server.set_json_response(
path="/apis/kubevirt.io/v1/namespaces/default/virtualmachineinstances",
code=200,
body={
"apiVersion": "v1",
"items": [
{
"apiVersion": "kubevirt.io/v1",
"kind": "VirtualMachineInstance",
"metadata": {
"annotations": {
"kubevirt.io/latest-observed-api-version": "v1",
"kubevirt.io/storage-observed-api-version": "v1alpha3",
},
"creationTimestamp": "2022-09-14T13:43:36Z",
"finalizers": [
"kubevirt.io/virtualMachineControllerFinalize",
"foregroundDeleteVirtualMachine",
],
"generation": 9,
"labels": {
"kubevirt.io/nodeName": "node01",
"kubevirt.io/vm": "vm-cirros",
},
"name": "vm-cirros",
"namespace": "default",
"ownerReferences": [
{
"apiVersion": "kubevirt.io/v1",
"blockOwnerDeletion": True,
"controller": True,
"kind": "VirtualMachine",
"name": "vm-cirros",
"uid": "4d1b1438-91ba-4c75-a211-566fc50a06f5",
}
],
"resourceVersion": "5387",
"uid": "7b3a8d94-bd7e-4c14-818a-89228172e4f1",
},
"spec": {
"domain": {
"cpu": {
"cores": 1,
"model": "host-model",
"sockets": 1,
"threads": 1,
},
"devices": {
"disks": [
{
"disk": {"bus": "virtio"},
"name": "containerdisk",
},
{
"disk": {"bus": "virtio"},
"name": "cloudinitdisk",
},
],
"interfaces": [{"bridge": {}, "name": "default"}],
},
"features": {"acpi": {"enabled": True}},
"firmware": {
"uuid": "0d2a2043-41c0-59c3-9b17-025022203668"
},
"machine": {"type": "q35"},
"resources": {"requests": {"memory": "128Mi"}},
},
"networks": [{"name": "default", "pod": {}}],
"terminationGracePeriodSeconds": 0,
"volumes": [
{
"containerDisk": {
"image": "registry:5000/kubevirt/cirros-container-disk-demo:devel",
"imagePullPolicy": "IfNotPresent",
},
"name": "containerdisk",
},
{
"cloudInitNoCloud": {
"userData": "#!/bin/sh\n\necho 'printed from cloud-init userdata'\n"
},
"name": "cloudinitdisk",
},
],
},
"status": {
"activePods": {
"a9a6c31b-8574-46f9-8bec-70ff091c3d97": "node01"
},
"conditions": [
{
"lastProbeTime": None,
"lastTransitionTime": "2022-09-14T13:43:39Z",
"status": "True",
"type": "Ready",
},
{
"lastProbeTime": None,
"lastTransitionTime": None,
"message": "cannot migrate VMI which does not use masquerade to connect to the pod network",
"reason": "InterfaceNotLiveMigratable",
"status": "False",
"type": "LiveMigratable",
},
],
"guestOSInfo": {},
"interfaces": [
{
"infoSource": "domain",
"ipAddress": "10.244.196.152",
"ipAddresses": ["10.244.196.152", "fd10:244::c497"],
"mac": "96:13:92:4f:05:d3",
"name": "default",
"queueCount": 1,
}
],
"launcherContainerImageVersion":
"registry:5000/kubevirt/virt-launcher@sha256:5c1474d240488c9a8e6e6e48b2ad446113744353b4cd2464baee3550e6b1829d",
"migrationMethod": "BlockMigration",
"migrationTransport": "Unix",
"nodeName": "node01",
"phase": "Running",
"phaseTransitionTimestamps": [
{
"phase": "Pending",
"phaseTransitionTimestamp": "2022-09-14T13:43:36Z",
},
{
"phase": "Scheduling",
"phaseTransitionTimestamp": "2022-09-14T13:43:36Z",
},
{
"phase": "Scheduled",
"phaseTransitionTimestamp": "2022-09-14T13:43:39Z",
},
{
"phase": "Running",
"phaseTransitionTimestamp": "2022-09-14T13:43:40Z",
},
],
"qosClass": "Burstable",
"runtimeUser": 0,
"virtualMachineRevisionName": "revision-start-vm-4d1b1438-91ba-4c75-a211-566fc50a06f5-9",
"volumeStatus": [
{"name": "cloudinitdisk", "size": 1048576, "target": "vdb"},
{"name": "containerdisk", "target": "vda"},
],
},
}
],
"kind": "List",
"metadata": {"resourceVersion": "", "selfLink": ""},
},
)

View File

@@ -0,0 +1,4 @@
plugin: kubevirt.core.kubevirt
connections:
- namespaces:
- default

View File

@@ -0,0 +1,5 @@
plugin: kubevirt.core.kubevirt
connections:
- namespaces:
- default
label_selector: app=test

View File

@@ -0,0 +1,5 @@
plugin: kubevirt.core.kubevirt
connections:
- namespaces:
- default
network_name: bridge-network

View File

@@ -1,124 +0,0 @@
{
"_meta": {
"hostvars": {
"default-vm-cirros": {
"annotations": {
"kubevirt.io/latest-observed-api-version": "v1",
"kubevirt.io/storage-observed-api-version": "v1alpha3"
},
"ansible_connection": "ssh",
"ansible_host": "10.244.196.152",
"cluster_name": null,
"labels": {
"kubevirt.io/nodeName": "node01",
"kubevirt.io/vm": "vm-cirros"
},
"object_type": "vmi",
"resource_version": "5387",
"uid": "7b3a8d94-bd7e-4c14-818a-89228172e4f1",
"vmi_active_pods": {
"a9a6c31b-8574-46f9-8bec-70ff091c3d97": "node01"
},
"vmi_conditions": [
{
"lastProbeTime": null,
"lastTransitionTime": "2022-09-14T13:43:39Z",
"status": "True",
"type": "Ready"
},
{
"lastProbeTime": null,
"lastTransitionTime": null,
"message": "cannot migrate VMI which does not use masquerade to connect to the pod network",
"reason": "InterfaceNotLiveMigratable",
"status": "False",
"type": "LiveMigratable"
}
],
"vmi_guest_os_info": {},
"vmi_interfaces": [
{
"infoSource": "domain",
"ipAddress": "10.244.196.152",
"ipAddresses": [
"10.244.196.152",
"fd10:244::c497"
],
"mac": "96:13:92:4f:05:d3",
"name": "default",
"queueCount": 1
}
],
"vmi_launcher_container_image_version": "registry:5000/kubevirt/virt-launcher@sha256:5c1474d240488c9a8e6e6e48b2ad446113744353b4cd2464baee3550e6b1829d",
"vmi_migration_method": "BlockMigration",
"vmi_migration_transport": "Unix",
"vmi_node_name": "node01",
"vmi_phase": "Running",
"vmi_phase_transition_timestamps": [
{
"phase": "Pending",
"phaseTransitionTimestamp": "2022-09-14T13:43:36Z"
},
{
"phase": "Scheduling",
"phaseTransitionTimestamp": "2022-09-14T13:43:36Z"
},
{
"phase": "Scheduled",
"phaseTransitionTimestamp": "2022-09-14T13:43:39Z"
},
{
"phase": "Running",
"phaseTransitionTimestamp": "2022-09-14T13:43:40Z"
}
],
"vmi_qos_class": "Burstable",
"vmi_virtual_machine_revision_name": "revision-start-vm-4d1b1438-91ba-4c75-a211-566fc50a06f5-9",
"vmi_volume_status": [
{
"name": "cloudinitdisk",
"size": 1048576,
"target": "vdb"
},
{
"name": "containerdisk",
"target": "vda"
}
]
}
}
},
"all": {
"children": [
"ungrouped",
"localhost_12345",
"label_kubevirt_io_nodeName_node01",
"label_kubevirt_io_vm_vm_cirros"
]
},
"label_kubevirt_io_nodeName_node01": {
"hosts": [
"default-vm-cirros"
]
},
"label_kubevirt_io_vm_vm_cirros": {
"hosts": [
"default-vm-cirros"
]
},
"localhost_12345": {
"children": [
"namespace_default"
]
},
"namespace_default": {
"children": [
"namespace_default_vmis"
]
},
"namespace_default_vmis": {
"hosts": [
"default-vm-cirros"
]
}
}

View File

@@ -0,0 +1,35 @@
---
- name: Verify inventory
connection: local
gather_facts: false
hosts: localhost
tasks:
- name: Read empty inventory
ansible.builtin.include_vars:
file: empty.yml
name: inv_empty
- name: Read inventory after vm creation
ansible.builtin.include_vars:
file: all.yml
name: inv_all
- name: Assert two instances with different labels
ansible.builtin.assert:
that:
- inv_all['all']['children']['label_app_test']['hosts'] | length == 1
- inv_all['all']['children']['label_foo_bar']['hosts'] | length == 1
- name: Read filtered inventory
ansible.builtin.include_vars:
file: label.yml
name: inv_label
- name: Assert one instance with selected label
ansible.builtin.assert:
that:
- inv_label['all']['children']['label_app_test']['hosts'] | length == 1
- name: Read filtered inventory
ansible.builtin.include_vars:
file: net.yml
name: inv_net
- name: Assert one instance with selected network
ansible.builtin.assert:
that:
- inv_net['all']['children']['label_app_test']['hosts'] | length == 1

View File

@@ -0,0 +1,43 @@
---
- name: Create VM
connection: local
gather_facts: false
hosts: localhost
tasks:
- name: Create a VirtualMachine
kubevirt.core.kubevirt_vm:
state: present
name: testvm3
namespace: default
labels:
app: test
wait: true
wait_timeout: 600
spec:
domain:
cpu:
sockets: 1
memory:
guest: 1536Mi
devices:
interfaces:
- name: default
masquerade: {}
- name: bridge-network
bridge: {}
networks:
- name: default
pod: {}
- name: bridge-network
multus:
networkName: kindexgw
volumes:
- containerDisk:
image: quay.io/containerdisks/centos-stream:9
name: containerdisk
- cloudInitNoCloud:
userData: |-
#cloud-config
ssh_authorized_keys:
- {{ lookup('file', 'pub_key') }}
name: cloudinit

View File

@@ -0,0 +1,21 @@
#!/usr/bin/env bash
set -eux
set -o pipefail
{
export ANSIBLE_CALLBACKS_ENABLED=profile_tasks
export ANSIBLE_INVENTORY_ENABLED=kubevirt.core.kubevirt,yaml
[ -d files ] || mkdir files
[ -f files/priv_key ] || (ssh-keygen -t ed25519 -C test@test -f files/priv_key ; ssh-keygen -y -f files/priv_key > files/pub_key)
ansible-playbook playbook.yml "$@"
ansible-inventory -i test.kubevirt.yml -y --list "$@"
ansible-playbook verify.yml -i test.kubevirt.yml --private-key=files/priv_key "$@"
rm "$HOME/.ssh/known_hosts"
} || {
exit 1
}

View File

@@ -0,0 +1,5 @@
plugin: kubevirt.core.kubevirt
connections:
- namespaces:
- default
network_name: bridge-network

View File

@@ -0,0 +1,69 @@
---
- name: Wait for ssh
connection: local
gather_facts: false
hosts: localhost
tasks:
- name: Wait up to 900 seconds for port 22 to become open
ansible.builtin.wait_for:
port: 22
host: "{{ hostvars['default-testvm3'].ansible_host }}"
delay: 30
timeout: 900
- name: Connect to VM
gather_facts: true
hosts: default-testvm3
remote_user: centos
vars:
ansible_python_interpreter: /usr/bin/python3
tasks:
- name: Print out virtual machine facts
ansible.builtin.debug:
var: ansible_facts
- name: Verify creation with existing VM
connection: local
gather_facts: false
hosts: localhost
tasks:
- name: Create a VirtualMachine
kubevirt.core.kubevirt_vm:
state: present
name: testvm3
namespace: default
labels:
app: test
register: recreate
- name: Assert module reported no changes
ansible.builtin.assert:
that:
- not recreate.changed
- name: Delete VM
connection: local
gather_facts: false
hosts: localhost
tasks:
- name: Delete a VirtualMachine
kubevirt.core.kubevirt_vm:
state: absent
name: testvm3
namespace: default
wait: true
- name: Verify VM deletion
connection: local
gather_facts: false
hosts: localhost
tasks:
- name: Delete a VirtualMachine
kubevirt.core.kubevirt_vm:
state: absent
name: testvm3
namespace: default
register: delete
- name: Assert module reported no changes
ansible.builtin.assert:
that:
- not delete.changed