Skip to content

Commit

Permalink
Add unittest coverage for retry httperror
Browse files Browse the repository at this point in the history
  • Loading branch information
ricolin committed Nov 1, 2024
1 parent 1363f2d commit 88bb226
Show file tree
Hide file tree
Showing 5 changed files with 343 additions and 4 deletions.
28 changes: 28 additions & 0 deletions .github/workflows/unittests.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
name: unittests

on:
workflow_dispatch:
push:
branches:
- 'stable/1.0'
tags:
- 'v*'
pull_request:
branches:
- 'stable/1.0'

jobs:
unuttest:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3

- name: Setup Python
uses: actions/setup-python@v4

- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install -y tox
- name: Run tox -e py3
run: tox -e py3
7 changes: 7 additions & 0 deletions staffeln/common/openstack.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ def set_project(self, project):
@tenacity.retry(
retry=RetryHTTPError(),
wait=tenacity.wait_exponential(max=30),
reraise=True,
stop=tenacity.stop_after_delay(CONF.conductor.retry_timeout))
def get_user_id(self):
user_name = self.conn.config.auth["username"]
Expand All @@ -65,13 +66,15 @@ def get_user_id(self):
@tenacity.retry(
retry=RetryHTTPError(),
wait=tenacity.wait_exponential(max=30),
reraise=True,
stop=tenacity.stop_after_delay(CONF.conductor.retry_timeout))
def get_projects(self):
return self.conn.list_projects()

@tenacity.retry(
retry=RetryHTTPError(),
wait=tenacity.wait_exponential(max=30),
reraise=True,
stop=tenacity.stop_after_delay(CONF.conductor.retry_timeout))
def get_servers(self, project_id=None, all_projects=True, details=True):
if project_id is not None:
Expand All @@ -84,13 +87,15 @@ def get_servers(self, project_id=None, all_projects=True, details=True):
@tenacity.retry(
retry=RetryHTTPError(),
wait=tenacity.wait_exponential(max=30),
reraise=True,
stop=tenacity.stop_after_delay(CONF.conductor.retry_timeout))
def get_volume(self, uuid, project_id):
return self.conn.get_volume_by_id(uuid)

@tenacity.retry(
retry=RetryHTTPError(),
wait=tenacity.wait_exponential(max=30),
reraise=True,
stop=tenacity.stop_after_delay(CONF.conductor.retry_timeout))
def get_backup(self, uuid, project_id=None):
# return conn.block_storage.get_backup(
Expand All @@ -115,6 +120,7 @@ def create_backup(self, volume_id, project_id, force=True, wait=False):
@tenacity.retry(
retry=RetryHTTPError(),
wait=tenacity.wait_exponential(max=30),
reraise=True,
stop=tenacity.stop_after_delay(CONF.conductor.retry_timeout))
def delete_backup(self, uuid, project_id=None, force=False):
# Note(Alex): v3 is not supporting force delete?
Expand All @@ -130,6 +136,7 @@ def delete_backup(self, uuid, project_id=None, force=False):
@tenacity.retry(
retry=RetryHTTPError(),
wait=tenacity.wait_exponential(max=30),
reraise=True,
stop=tenacity.stop_after_delay(CONF.conductor.retry_timeout))
def get_backup_quota(self, project_id):
# quota = conn.get_volume_quotas(project_id)
Expand Down
Empty file.
304 changes: 304 additions & 0 deletions staffeln/tests/common/test_openstacksdk.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,304 @@
# SPDX-License-Identifier: Apache-2.0
from __future__ import annotations

from unittest import mock

from openstack import exceptions as openstack_exc
import tenacity

from staffeln.common import openstack as s_openstack
from staffeln.tests import base


class OpenstackSDKTest(base.TestCase):

def setUp(self):
super(OpenstackSDKTest, self).setUp()
self.m_c = mock.MagicMock()
with mock.patch("openstack.connect", return_value=self.m_c):
self.openstack = s_openstack.OpenstackSDK()
self.m_sleep = mock.Mock()
func_list = [
"get_user_id",
"get_projects",
"get_servers",
"get_volume",
"get_backup",
"delete_backup",
"get_backup_quota",
]
for i in func_list:
getattr(self.openstack, i).retry.sleep = ( # pylint: disable=E1101
self.m_sleep
)
getattr(self.openstack, i).retry.stop = ( # pylint: disable=E1101
tenacity.stop_after_attempt(2)
)

self.fake_user = mock.MagicMock(id="foo", email="[email protected]")
self.fake_volume = mock.MagicMock(id="fake_volume")
self.fake_backup = mock.MagicMock(id="fake_backup")
self.fake_role_assignment = mock.MagicMock(user="foo")
self.fake_role_assignment2 = mock.MagicMock(user={"id": "bar"})

def _test_http_error(
self, m_func, retry_func, status_code, call_count=1, **kwargs
):
m_func.side_effect = openstack_exc.HttpException(
http_status=status_code
)
exc = self.assertRaises(
openstack_exc.HttpException,
getattr(self.openstack, retry_func),
**kwargs,
)
self.assertEqual(status_code, exc.status_code)
if status_code != 404:
if call_count == 1:
self.m_sleep.assert_called_once_with(1.0)
else:
self.m_sleep.assert_has_calls(
[mock.call(1.0) for c in range(call_count)]
)
else:
self.m_sleep.assert_not_called()

def _test_non_http_error(self, m_func, retry_func, **kwargs):
m_func.side_effect = KeyError
self.assertRaises(
KeyError, getattr(self.openstack, retry_func), **kwargs
)
self.m_sleep.assert_not_called()

def test_get_servers(self):
self.m_c.compute.servers = mock.MagicMock(return_value=[])
self.assertEqual(self.openstack.get_servers(), [])
self.m_c.compute.servers.assert_called_once_with(
details=True, all_projects=True
)

def test_get_servers_non_http_error(self):
self._test_non_http_error(self.m_c.compute.servers, "get_servers")

def test_get_servers_404_http_error(self):
self._test_http_error(
self.m_c.compute.servers, "get_servers", status_code=404
)

def test_get_servers_500_http_error(self):
self._test_http_error(
self.m_c.compute.servers, "get_servers", status_code=500
)

def test_get_projects(self):
self.m_c.list_projects = mock.MagicMock(return_value=[])
self.assertEqual(self.openstack.get_projects(), [])
self.m_c.list_projects.assert_called_once_with()

def test_get_projects_non_http_error(self):
self._test_non_http_error(self.m_c.list_projects, "get_projects")

def test_get_projects_404_http_error(self):
self._test_http_error(
self.m_c.list_projects, "get_projects", status_code=404
)

def test_get_projects_500_http_error(self):
self._test_http_error(
self.m_c.list_projects, "get_projects", status_code=500
)

def test_get_user_id(self):
self.m_c.get_user = mock.MagicMock(return_value=self.fake_user)
self.assertEqual(self.openstack.get_user_id(), "foo")
self.m_c.get_user.assert_called_once_with(name_or_id=mock.ANY)

def test_get_user_id_non_http_error(self):
self._test_non_http_error(self.m_c.get_user, "get_user_id")

def test_get_user_id_404_http_error(self):
self._test_http_error(
self.m_c.get_user, "get_user_id", status_code=404
)

def test_get_user_id_500_http_error(self):
self._test_http_error(
self.m_c.get_user, "get_user_id", status_code=500
)

def test_get_volume(self):
self.m_c.get_volume_by_id = mock.MagicMock(
return_value=self.fake_volume
)
self.assertEqual(
self.openstack.get_volume(
uuid=self.fake_volume.id, project_id="bar"
),
self.fake_volume,
)
self.m_c.get_volume_by_id.assert_called_once_with(self.fake_volume.id)

def test_get_volume_non_http_error(self):
self._test_non_http_error(
self.m_c.get_volume_by_id,
"get_volume",
uuid="foo",
project_id="bar",
)

def test_get_volume_404_http_error(self):
self._test_http_error(
self.m_c.get_volume_by_id,
"get_volume",
status_code=404,
uuid="foo",
project_id="bar",
)

def test_get_volume_500_http_error(self):
self._test_http_error(
self.m_c.get_volume_by_id,
"get_volume",
status_code=500,
uuid="foo",
project_id="bar",
)

def test_get_backup(self):
self.m_c.get_volume_backup = mock.MagicMock(
return_value=self.fake_backup
)
self.assertEqual(
self.openstack.get_backup(
uuid=self.fake_backup.id, project_id="bar"
),
self.fake_backup,
)
self.m_c.get_volume_backup.assert_called_once_with(self.fake_backup.id)

def test_get_backup_not_found(self):
self.m_c.get_volume_backup = mock.MagicMock(
side_effect=openstack_exc.ResourceNotFound
)
self.assertEqual(
self.openstack.get_backup(
uuid=self.fake_backup.id, project_id="bar"
),
None,
)
self.m_c.get_volume_backup.assert_called_once_with(self.fake_backup.id)

def test_get_backup_non_http_error(self):
self._test_non_http_error(
self.m_c.get_volume_backup,
"get_backup",
uuid="foo",
project_id="bar",
)

def test_get_backup_404_http_error(self):
self._test_http_error(
self.m_c.get_volume_backup,
"get_backup",
status_code=404,
uuid="foo",
project_id="bar",
)

def test_get_backup_500_http_error(self):
self._test_http_error(
self.m_c.get_volume_backup,
"get_backup",
status_code=500,
uuid="foo",
project_id="bar",
)

def test_delete_backup(self):
self.m_c.delete_volume_backup = mock.MagicMock(
return_value=self.fake_backup
)
self.assertEqual(
self.openstack.delete_backup(
uuid=self.fake_backup.id, project_id="bar"
),
None,
)
self.m_c.delete_volume_backup.assert_called_once_with(
self.fake_backup.id, force=False
)

def test_delete_backup_not_found(self):
self.m_c.delete_volume_backup = mock.MagicMock(
side_effect=openstack_exc.ResourceNotFound
)
self.assertEqual(
self.openstack.delete_backup(
uuid=self.fake_backup.id, project_id="bar"
),
None,
)
self.m_c.delete_volume_backup.assert_called_once_with(
self.fake_backup.id, force=False
)

def test_delete_backup_non_http_error(self):
self._test_non_http_error(
self.m_c.delete_volume_backup,
"delete_backup",
uuid="foo",
project_id="bar",
)

def test_delete_backup_404_http_error(self):
self._test_http_error(
self.m_c.delete_volume_backup,
"delete_backup",
status_code=404,
uuid="foo",
project_id="bar",
)

def test_delete_backup_500_http_error(self):
self._test_http_error(
self.m_c.delete_volume_backup,
"delete_backup",
status_code=500,
uuid="foo",
project_id="bar",
)

@mock.patch("openstack.proxy._json_response")
def test_get_backup_quota(self, m_j_r):
self.m_c.block_storage.get = mock.MagicMock(status_code=200)
self.m_gam = mock.MagicMock()
self.m_c._get_and_munchify = self.m_gam
self.m_gam.return_value = mock.MagicMock(backups=[self.fake_backup.id])
self.assertEqual(
[self.fake_backup.id],
self.openstack.get_backup_quota(project_id="bar"),
)
self.m_c.block_storage.get.assert_called_once_with(
"/os-quota-sets/bar?usage=True"
)

def test_get_backup_quota_non_http_error(self):
self._test_non_http_error(
self.m_c.block_storage.get, "get_backup_quota", project_id="bar"
)

def test_get_backup_quota_404_http_error(self):
self._test_http_error(
self.m_c.block_storage.get,
"get_backup_quota",
status_code=404,
project_id="bar",
)

def test_get_backup_quota_500_http_error(self):
self._test_http_error(
self.m_c.block_storage.get,
"get_backup_quota",
status_code=500,
project_id="bar",
)
Loading

0 comments on commit 88bb226

Please sign in to comment.