placement/placement/tests/unit/handlers/test_util.py

292 lines
14 KiB
Python

#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""Unit tests for the utility functions used by the placement DB."""
import fixtures
import microversion_parse
from oslo_config import cfg
from oslo_config import fixture as config_fixture
from oslo_utils.fixture import uuidsentinel
import webob
from placement import conf
from placement import context
from placement import exception
from placement.handlers import util
from placement import microversion
from placement.objects import consumer as consumer_obj
from placement.objects import consumer_type as consumer_type_obj
from placement.objects import project as project_obj
from placement.objects import user as user_obj
from placement.tests.unit import base
class TestEnsureConsumer(base.ContextTestCase):
def setUp(self):
super(TestEnsureConsumer, self).setUp()
self.conf = cfg.ConfigOpts()
self.useFixture(config_fixture.Config(self.conf))
conf.register_opts(self.conf)
self.mock_project_get = self.useFixture(fixtures.MockPatch(
'placement.objects.project.'
'Project.get_by_external_id')).mock
self.mock_user_get = self.useFixture(fixtures.MockPatch(
'placement.objects.user.'
'User.get_by_external_id')).mock
self.mock_consumer_get = self.useFixture(fixtures.MockPatch(
'placement.objects.consumer.'
'Consumer.get_by_uuid')).mock
self.mock_project_create = self.useFixture(fixtures.MockPatch(
'placement.objects.project.'
'Project.create')).mock
self.mock_user_create = self.useFixture(fixtures.MockPatch(
'placement.objects.user.'
'User.create')).mock
self.mock_consumer_create = self.useFixture(fixtures.MockPatch(
'placement.objects.consumer.'
'Consumer.create')).mock
self.mock_consumer_update = self.useFixture(fixtures.MockPatch(
'placement.objects.consumer.'
'Consumer.update')).mock
self.mock_consumer_type_get = self.useFixture(fixtures.MockPatch(
'placement.objects.consumer_type.'
'ConsumerType.get_by_name')).mock
self.ctx = context.RequestContext(user_id='fake', project_id='fake')
self.ctx.config = self.conf
self.consumer_id = uuidsentinel.consumer
self.project_id = uuidsentinel.project
self.user_id = uuidsentinel.user
mv_parsed = microversion_parse.Version(1, 27)
mv_parsed.max_version = microversion_parse.parse_version_string(
microversion.max_version_string())
mv_parsed.min_version = microversion_parse.parse_version_string(
microversion.min_version_string())
self.before_version = mv_parsed
mv_parsed = microversion_parse.Version(1, 28)
mv_parsed.max_version = microversion_parse.parse_version_string(
microversion.max_version_string())
mv_parsed.min_version = microversion_parse.parse_version_string(
microversion.min_version_string())
self.after_version = mv_parsed
mv_parsed = microversion_parse.Version(1, 38)
mv_parsed.max_version = microversion_parse.parse_version_string(
microversion.max_version_string())
mv_parsed.min_version = microversion_parse.parse_version_string(
microversion.min_version_string())
self.cons_type_req_version = mv_parsed
def test_no_existing_project_user_consumer_before_gen_success(self):
"""Tests that we don't require a consumer_generation=None before the
appropriate microversion.
"""
self.mock_project_get.side_effect = exception.NotFound
self.mock_user_get.side_effect = exception.NotFound
self.mock_consumer_get.side_effect = exception.NotFound
consumer_gen = 1 # should be ignored
util.ensure_consumer(
self.ctx, self.consumer_id, self.project_id, self.user_id,
consumer_gen, 'TYPE', self.before_version)
self.mock_project_get.assert_called_once_with(
self.ctx, self.project_id)
self.mock_user_get.assert_called_once_with(
self.ctx, self.user_id)
self.mock_consumer_get.assert_called_once_with(
self.ctx, self.consumer_id)
self.mock_project_create.assert_called_once()
self.mock_user_create.assert_called_once()
self.mock_consumer_create.assert_called_once()
def test_no_existing_project_user_consumer_after_gen_success(self):
"""Tests that we require a consumer_generation=None after the
appropriate microversion.
"""
self.mock_project_get.side_effect = exception.NotFound
self.mock_user_get.side_effect = exception.NotFound
self.mock_consumer_get.side_effect = exception.NotFound
consumer_gen = None # should NOT be ignored (and None is expected)
util.ensure_consumer(
self.ctx, self.consumer_id, self.project_id, self.user_id,
consumer_gen, 'TYPE', self.after_version)
self.mock_project_get.assert_called_once_with(
self.ctx, self.project_id)
self.mock_user_get.assert_called_once_with(
self.ctx, self.user_id)
self.mock_consumer_get.assert_called_once_with(
self.ctx, self.consumer_id)
self.mock_project_create.assert_called_once()
self.mock_user_create.assert_called_once()
self.mock_consumer_create.assert_called_once()
def test_no_existing_project_user_consumer_after_gen_fail(self):
"""Tests that we require a consumer_generation=None after the
appropriate microversion and that None is the expected value.
"""
self.mock_project_get.side_effect = exception.NotFound
self.mock_user_get.side_effect = exception.NotFound
self.mock_consumer_get.side_effect = exception.NotFound
consumer_gen = 1 # should NOT be ignored (and 1 is not expected)
self.assertRaises(
webob.exc.HTTPConflict,
util.ensure_consumer,
self.ctx, self.consumer_id, self.project_id, self.user_id,
consumer_gen, 'TYPE', self.after_version)
def test_no_existing_project_user_consumer_use_incomplete(self):
"""Verify that if the project_id arg is None, that we fall back to the
CONF options for incomplete project and user ID.
"""
self.mock_project_get.side_effect = exception.NotFound
self.mock_user_get.side_effect = exception.NotFound
self.mock_consumer_get.side_effect = exception.NotFound
consumer_gen = None # should NOT be ignored (and None is expected)
util.ensure_consumer(
self.ctx, self.consumer_id, None, None,
consumer_gen, 'TYPE', self.before_version)
self.mock_project_get.assert_called_once_with(
self.ctx, self.conf.placement.incomplete_consumer_project_id)
self.mock_user_get.assert_called_once_with(
self.ctx, self.conf.placement.incomplete_consumer_user_id)
self.mock_consumer_get.assert_called_once_with(
self.ctx, self.consumer_id)
self.mock_project_create.assert_called_once()
self.mock_user_create.assert_called_once()
self.mock_consumer_create.assert_called_once()
def test_existing_project_no_existing_consumer_before_gen_success(self):
"""Check that if we find an existing project and user, that we use
those found objects in creating the consumer. Do not require a consumer
generation before the appropriate microversion.
"""
proj = project_obj.Project(self.ctx, id=1, external_id=self.project_id)
self.mock_project_get.return_value = proj
user = user_obj.User(self.ctx, id=1, external_id=self.user_id)
self.mock_user_get.return_value = user
self.mock_consumer_get.side_effect = exception.NotFound
consumer_gen = None # should be ignored
util.ensure_consumer(
self.ctx, self.consumer_id, self.project_id, self.user_id,
consumer_gen, 'TYPE', self.before_version)
self.mock_project_create.assert_not_called()
self.mock_user_create.assert_not_called()
self.mock_consumer_create.assert_called_once()
def test_existing_consumer_after_gen_matches_supplied_gen(self):
"""Tests that we require a consumer_generation after the
appropriate microversion and that when the consumer already exists,
then we ensure a matching generation is supplied
"""
proj = project_obj.Project(self.ctx, id=1, external_id=self.project_id)
self.mock_project_get.return_value = proj
user = user_obj.User(self.ctx, id=1, external_id=self.user_id)
self.mock_user_get.return_value = user
consumer = consumer_obj.Consumer(
self.ctx, id=1, project=proj, user=user, generation=2)
self.mock_consumer_get.return_value = consumer
consumer_gen = 2 # should NOT be ignored (and 2 is expected)
util.ensure_consumer(
self.ctx, self.consumer_id, self.project_id, self.user_id,
consumer_gen, 'TYPE', self.after_version)
self.mock_project_create.assert_not_called()
self.mock_user_create.assert_not_called()
self.mock_consumer_create.assert_not_called()
def test_existing_consumer_after_gen_fail(self):
"""Tests that we require a consumer_generation after the
appropriate microversion and that when the consumer already exists,
then we raise a 400 when there is a mismatch on the existing
generation.
"""
proj = project_obj.Project(self.ctx, id=1, external_id=self.project_id)
self.mock_project_get.return_value = proj
user = user_obj.User(self.ctx, id=1, external_id=self.user_id)
self.mock_user_get.return_value = user
consumer = consumer_obj.Consumer(
self.ctx, id=1, project=proj, user=user, generation=42)
self.mock_consumer_get.return_value = consumer
consumer_gen = 2 # should NOT be ignored (and 2 is NOT expected)
self.assertRaises(
webob.exc.HTTPConflict,
util.ensure_consumer,
self.ctx, self.consumer_id, self.project_id, self.user_id,
consumer_gen, 'TYPE', self.after_version)
def test_existing_consumer_different_consumer_type_supplied(self):
"""Tests that we update a consumer's type ID if the one supplied by the
user is different than the one in the existing record.
"""
proj = project_obj.Project(self.ctx, id=1, external_id=self.project_id)
self.mock_project_get.return_value = proj
user = user_obj.User(self.ctx, id=1, external_id=self.user_id)
self.mock_user_get.return_value = user
# Consumer currently has type ID = 1
consumer = consumer_obj.Consumer(
self.ctx, id=1, project=proj, user=user, generation=1,
consumer_type_id=1)
self.mock_consumer_get.return_value = consumer
# Supplied consumer type ID = 2
consumer_type = consumer_type_obj.ConsumerType(
self.ctx, id=2, name='TYPE')
self.mock_consumer_type_get.return_value = consumer_type
consumer_gen = 1
util.ensure_consumer(
self.ctx, self.consumer_id, self.project_id, self.user_id,
consumer_gen, 'TYPE', self.cons_type_req_version)
# Expect 1 call to update() to update to the supplied consumer type ID
self.mock_consumer_update.assert_called_once_with()
# Consumer should have the new consumer type ID = 2
self.assertEqual(2, consumer.consumer_type_id)
def test_consumer_create_exists_different_consumer_type_supplied(self):
"""Tests that we update a consumer's type ID if the one supplied by a
racing request is different than the one in the existing (recently
created) record.
"""
proj = project_obj.Project(self.ctx, id=1, external_id=self.project_id)
self.mock_project_get.return_value = proj
user = user_obj.User(self.ctx, id=1, external_id=self.user_id)
self.mock_user_get.return_value = user
# Request A recently created consumer has type ID = 1
consumer = consumer_obj.Consumer(
self.ctx, id=1, project=proj, user=user, generation=1,
consumer_type_id=1, uuid=uuidsentinel.consumer)
self.mock_consumer_get.return_value = consumer
# Request B supplied consumer type ID = 2
consumer_type = consumer_type_obj.ConsumerType(
self.ctx, id=2, name='TYPE')
self.mock_consumer_type_get.return_value = consumer_type
# Request B will encounter ConsumerExists as Request A just created it
self.mock_consumer_create.side_effect = (
exception.ConsumerExists(uuid=uuidsentinel.consumer))
consumer_gen = 1
util.ensure_consumer(
self.ctx, self.consumer_id, self.project_id, self.user_id,
consumer_gen, 'TYPE', self.cons_type_req_version)
# Expect 1 call to update() to update to the supplied consumer type ID
self.mock_consumer_update.assert_called_once_with()
# Consumer should have the new consumer type ID = 2
self.assertEqual(2, consumer.consumer_type_id)