ovirt-imageio/test/proxy_test.py
Albert Esteve fc6e58d9e6 reuse: addheader test/*.py
Add SPDX header to python files with
the 'py' extension in the test directory.

Signed-off-by: Albert Esteve <aesteve@redhat.com>
2022-10-18 13:04:20 +02:00

359 lines
9.5 KiB
Python

# SPDX-FileCopyrightText: Red Hat, Inc.
# SPDX-License-Identifier: GPL-2.0-or-later
import json
import pytest
from ovirt_imageio._internal import config
from ovirt_imageio._internal import server
from . import http
from . import testutil
@pytest.fixture(scope="module")
def daemon():
daemon = server.Server(config.load(["test/conf/daemon.conf"]))
daemon.start()
yield daemon
daemon.stop()
@pytest.fixture(scope="module")
def proxy():
proxy = server.Server(config.load(["test/conf/proxy.conf"]))
proxy.start()
yield proxy
proxy.stop()
def test_local_service_disabled(proxy):
assert proxy.local_service is None
def test_images_extents(daemon, proxy, tmpfile):
# Get image extents via proxy.
size = 128 * 1024
with open(tmpfile, "wb") as f:
f.truncate(size)
# Add tickets.
ticket = testutil.create_ticket(
url="file://{}".format(tmpfile),
size=size)
daemon.auth.add(ticket)
proxy.auth.add(proxy_ticket(daemon, ticket))
# Get zero extents.
with http.RemoteClient(proxy.config) as c:
res = c.request("GET", "/images/{}/extents".format(ticket["uuid"]))
data = res.read()
assert res.status == 200
extents = json.loads(data)
assert extents == [
{"start": 0, "length": size, "zero": False, "hole": False}
]
@pytest.mark.parametrize("align", [-4096, 0, 4096])
def test_images_download_full(daemon, proxy, tmpfile, align):
# Simple download of entire image as done by stupid clients.
size = proxy.config.backend_file.buffer_size + align
data = b"x" * size
with open(tmpfile, "wb") as f:
f.write(data)
# Add daemon ticket serving tmpfile.
ticket = testutil.create_ticket(
url="file://{}".format(tmpfile),
size=size)
daemon.auth.add(ticket)
# Add proxy ticket, proxying request to daemon.
proxy.auth.add(proxy_ticket(daemon, ticket))
# Download complete image.
with http.RemoteClient(proxy.config) as c:
res = c.request("GET", "/images/{}".format(ticket["uuid"]))
client_data = res.read()
assert res.status == 200
assert client_data == data
def test_images_download_options_error(daemon, proxy, tmpfile):
# Passing OPTIONS error from daemon to proxy client.
# Create a proxy ticket, but no daemon ticket.
ticket = testutil.create_ticket(
url="file://{}".format(tmpfile),
size=4096)
proxy.auth.add(proxy_ticket(daemon, ticket))
# This request should fail in the proxy when opening the backend and
# sending OPTIONS request.
with http.RemoteClient(proxy.config) as c:
res = c.request("GET", "/images/{}".format(ticket["uuid"]))
res.read()
# The error should propagate to the caller.
assert res.status == 403
def test_images_download_get_error(daemon, proxy, tmpfile):
# Passing GET error from daemon to proxy client.
size = 128 * 1024
data = b"x" * size
with open(tmpfile, "wb") as f:
f.write(data)
# Add a daemon ticket with smaller size to trigger 416 error.
dt = testutil.create_ticket(
url="file://{}".format(tmpfile),
size=size - 4096)
daemon.auth.add(dt)
# Add proxy ticket with correct size to ensure the request will fail in the
# daemon.
pt = proxy_ticket(daemon, dt)
pt["size"] = size
proxy.auth.add(pt)
# This request should fail in the daemon when trying to read after the
# ticket size.
with http.RemoteClient(proxy.config) as c:
res = c.request("GET", "/images/{}".format(dt["uuid"]))
res.read()
# The error should propagate to the caller.
assert res.status == 416
def test_images_download_missing_image(daemon, proxy):
# Passing GET /extents error from daemon to proxy client.
# Add a daemon ticket with non-existing file, causing a failure when
# opening the backend in the daemon.
ticket = testutil.create_ticket(
url="file:///no/such/image",
size=128 * 1024)
daemon.auth.add(ticket)
proxy.auth.add(proxy_ticket(daemon, ticket))
with http.RemoteClient(proxy.config) as c:
res = c.request("GET", "/images/{}".format(ticket["uuid"]))
res.read()
# Pass the daemon error to proxy client.
assert res.status == 500
@pytest.mark.parametrize("align", [-4096, 0, 4096])
def test_images_upload_full(daemon, proxy, tmpfile, align):
# Simple upload of entire image as done by stupid clients.
size = proxy.config.backend_file.buffer_size + align
data = b"x" * size
# Create empty sparse image.
with open(tmpfile, "wb") as f:
f.truncate(size)
# Add daemon ticket serving tmpfile.
ticket = testutil.create_ticket(
url="file://{}".format(tmpfile),
size=size)
daemon.auth.add(ticket)
# Add proxy ticket, proxying request to daemon.
proxy.auth.add(proxy_ticket(daemon, ticket))
# Upload data to image.
with http.RemoteClient(proxy.config) as c:
res = c.request(
"PUT",
"/images/{}".format(ticket["uuid"]),
body=data)
res.read()
assert res.status == 200
with open(tmpfile, "rb") as f:
assert f.read() == data
def test_images_upload_error(daemon, proxy, tmpfile):
# Pass PUT error from daemon to proxy client.
size = 128 * 1024
data = b"x" * size
# Create empty sparse image.
with open(tmpfile, "wb") as f:
f.truncate(size)
# Add a daemon ticket with smaller size to trigger 416 error.
dt = testutil.create_ticket(
url="file://{}".format(tmpfile),
size=size - 4096)
daemon.auth.add(dt)
# Add proxy ticket with correct size to ensure the request will fail in the
# daemon.
pt = proxy_ticket(daemon, dt)
pt["size"] = size
proxy.auth.add(pt)
# Upload data to image.
with http.RemoteClient(proxy.config) as c:
res = c.request(
"PUT",
"/images/{}".format(dt["uuid"]),
body=data)
res.read()
assert res.status == 416
def test_images_zero(daemon, proxy, tmpfile):
# Zero entire image via proxy.
size = 128 * 1024
# Create image with data.
with open(tmpfile, "wb") as f:
f.write(b"x" * size)
# Add tickets.
ticket = testutil.create_ticket(
url="file://{}".format(tmpfile),
size=size)
daemon.auth.add(ticket)
proxy.auth.add(proxy_ticket(daemon, ticket))
# Zero entire image.
patch = {"op": "zero", "offset": 0, "size": size}
body = json.dumps(patch).encode("utf-8")
with http.RemoteClient(proxy.config) as c:
res = c.request(
"PATCH",
"/images/{}".format(ticket["uuid"]),
body=body)
res.read()
assert res.status == 200
with open(tmpfile, "rb") as f:
assert f.read() == b"\0" * size
def test_images_zero_error(daemon, proxy):
# Pass PATCH error from daemon to proxy client.
size = 128 * 1024
# Add tickets for non existing image.
ticket = testutil.create_ticket(
url="file:///no/such/image",
size=size)
daemon.auth.add(ticket)
proxy.auth.add(proxy_ticket(daemon, ticket))
# Zero entire image.
patch = {"op": "zero", "offset": 0, "size": size}
body = json.dumps(patch).encode("utf-8")
with http.RemoteClient(proxy.config) as c:
res = c.request(
"PATCH",
"/images/{}".format(ticket["uuid"]),
body=body)
res.read()
assert res.status == 500
def test_images_flush(daemon, proxy, tmpfile):
# Zero entire image via proxy.
size = 128 * 1024
# Create empty sparse image.
with open(tmpfile, "wb") as f:
f.truncate(size)
# Add tickets.
ticket = testutil.create_ticket(
url="file://{}".format(tmpfile),
size=size)
daemon.auth.add(ticket)
proxy.auth.add(proxy_ticket(daemon, ticket))
# Flush image.
patch = {"op": "flush"}
body = json.dumps(patch).encode("utf-8")
with http.RemoteClient(proxy.config) as c:
res = c.request(
"PATCH",
"/images/{}".format(ticket["uuid"]),
body=body)
res.read()
assert res.status == 200
@pytest.mark.parametrize("conf_files", [
# Using system PKI for both proxy and daemon (default oVirt setup).
pytest.param(["test/conf/proxy.conf"], id="system"),
# Using user PKI for proxy and system PKI for daemon (oVirt setup with user
# certificates).
pytest.param(
["test/conf/proxy.conf", "test/conf/user-tls.conf"], id="user")
])
def test_tls(daemon, tmpfile, conf_files):
size = daemon.config.backend_file.buffer_size
data = b"x" * size
with open(tmpfile, "wb") as f:
f.write(data)
# Add daemon ticket serving tmpfile.
ticket = testutil.create_ticket(
url="file://{}".format(tmpfile),
size=size)
daemon.auth.add(ticket)
proxy = server.Server(config.load(conf_files))
proxy.start()
try:
# Add proxy ticket, proxying request to daemon.
proxy.auth.add(proxy_ticket(daemon, ticket))
# Download complete image.
with http.RemoteClient(proxy.config) as c:
res = c.request("GET", "/images/{}".format(ticket["uuid"]))
client_data = res.read()
finally:
proxy.stop()
assert res.status == 200
assert client_data == data
def proxy_ticket(daemon, daemon_ticket):
"""
Create a proxy ticket from daemon ticket.
"""
ticket = dict(daemon_ticket)
host, port = daemon.remote_service.address
ticket["url"] = "https://{}:{}/images/{}".format(
host, port, ticket["uuid"])
return ticket