2022-10-06 13:33:45 +02:00
# SPDX-FileCopyrightText: Red Hat, Inc.
# SPDX-License-Identifier: GPL-2.0-or-later
2020-03-21 18:50:01 +02:00
2020-04-06 18:18:55 +03:00
import json
2020-03-21 18:50:01 +02:00
import pytest
2020-05-10 09:40:03 +03:00
from ovirt_imageio . _internal import config
from ovirt_imageio . _internal import server
2020-03-21 18:50:01 +02:00
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 ( )
2020-03-31 13:57:19 +02:00
def test_local_service_disabled ( proxy ) :
assert proxy . local_service is None
2020-04-06 18:18:55 +03:00
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 )
2020-06-09 21:37:07 +03:00
assert extents == [
{ " start " : 0 , " length " : size , " zero " : False , " hole " : False }
]
2020-04-06 18:18:55 +03:00
2020-03-25 23:36:49 +02:00
@pytest.mark.parametrize ( " align " , [ - 4096 , 0 , 4096 ] )
2020-03-25 18:16:44 +02:00
def test_images_download_full ( daemon , proxy , tmpfile , align ) :
2020-03-21 18:50:01 +02:00
# Simple download of entire image as done by stupid clients.
2020-08-09 18:01:58 +03:00
size = proxy . config . backend_file . buffer_size + align
2020-03-25 18:16:44 +02:00
data = b " x " * size
2020-03-21 18:50:01 +02:00
with open ( tmpfile , " wb " ) as f :
f . write ( data )
# Add daemon ticket serving tmpfile.
ticket = testutil . create_ticket (
url = " file:// {} " . format ( tmpfile ) ,
2020-03-25 18:16:44 +02:00
size = size )
2020-03-21 18:50:01 +02:00
daemon . auth . add ( ticket )
# Add proxy ticket, proxying request to daemon.
proxy . auth . add ( proxy_ticket ( daemon , ticket ) )
# Download complete image.
2020-04-01 21:32:39 +02:00
with http . RemoteClient ( proxy . config ) as c :
2020-03-21 18:50:01 +02:00
res = c . request ( " GET " , " /images/ {} " . format ( ticket [ " uuid " ] ) )
client_data = res . read ( )
assert res . status == 200
assert client_data == data
2020-04-06 13:32:58 +03:00
def test_images_download_options_error ( daemon , proxy , tmpfile ) :
# Passing OPTIONS error from daemon to proxy client.
2020-03-30 13:17:41 +03:00
# 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.
2020-04-01 21:32:39 +02:00
with http . RemoteClient ( proxy . config ) as c :
2020-03-30 13:17:41 +03:00
res = c . request ( " GET " , " /images/ {} " . format ( ticket [ " uuid " ] ) )
res . read ( )
# The error should propagate to the caller.
assert res . status == 403
2020-04-06 13:32:58 +03:00
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
2020-04-06 16:13:54 +03:00
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
2020-03-25 18:16:44 +02:00
@pytest.mark.parametrize ( " align " , [ - 4096 , 0 , 4096 ] )
def test_images_upload_full ( daemon , proxy , tmpfile , align ) :
2020-03-21 18:50:01 +02:00
# Simple upload of entire image as done by stupid clients.
2020-08-09 18:01:58 +03:00
size = proxy . config . backend_file . buffer_size + align
2020-03-25 18:16:44 +02:00
data = b " x " * size
2020-03-21 18:50:01 +02:00
# Create empty sparse image.
with open ( tmpfile , " wb " ) as f :
2020-03-25 18:16:44 +02:00
f . truncate ( size )
2020-03-21 18:50:01 +02:00
# Add daemon ticket serving tmpfile.
ticket = testutil . create_ticket (
url = " file:// {} " . format ( tmpfile ) ,
2020-03-25 18:16:44 +02:00
size = size )
2020-03-21 18:50:01 +02:00
daemon . auth . add ( ticket )
# Add proxy ticket, proxying request to daemon.
proxy . auth . add ( proxy_ticket ( daemon , ticket ) )
# Upload data to image.
2020-04-01 21:32:39 +02:00
with http . RemoteClient ( proxy . config ) as c :
2020-03-21 18:50:01 +02:00
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
2020-04-06 16:32:21 +03:00
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
2020-04-06 18:18:55 +03:00
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
2020-07-31 13:44:53 +02:00
@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 ) :
2020-08-09 18:01:58 +03:00
size = daemon . config . backend_file . buffer_size
2020-07-31 12:09:19 +02:00
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 )
2020-07-31 13:44:53 +02:00
proxy = server . Server ( config . load ( conf_files ) )
2020-07-31 12:09:19 +02:00
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
2020-03-21 18:50:01 +02:00
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