Merge "Store settings of Fuel UI Nodes page in DB"

This commit is contained in:
Jenkins 2015-12-01 13:05:52 +00:00 committed by Gerrit Code Review
commit 2e90f7a577
12 changed files with 548 additions and 168 deletions

View File

@ -12,6 +12,8 @@
# License for the specific language governing permissions and limitations
# under the License.
from oslo_serialization import jsonutils
from nailgun.api.v1.handlers.base import content
from nailgun.api.v1.handlers.base import DBSingletonHandler
from nailgun.api.v1.validators.master_node_settings \
@ -30,28 +32,51 @@ class MasterNodeSettingsHandler(DBSingletonHandler):
not_found_error = "Settings are not found in DB"
def _handle_stats_opt_in(self):
def _handle_stats_opt_in(self, settings_data=None):
"""Starts task on stats user creation or removal
if self.single.must_send_stats():
:param settings_data: dict with master node settings.
Current data from DB will be used if master_node_settings_data is None
"""
must_send = self.single.must_send_stats(
master_node_settings_data=settings_data)
if must_send:
logger.debug("Handling customer opt-in to sending statistics")
manager = CreateStatsUserTaskManager()
else:
logger.debug("Handling customer opt-out to sending statistics")
manager = RemoveStatsUserTaskManager()
try:
manager.execute()
except Exception:
logger.exception("Stats user operation failed")
@content
def PUT(self):
result = super(MasterNodeSettingsHandler, self).PUT()
self._handle_stats_opt_in()
def _get_new_opt_in_status(self):
"""Extracts opt in status from request
Returns None if no opt in status in the request
:return: bool or None
"""
data = self.checked_data(self.validator.validate_update)
return data.get('settings', {}).get('statistics', {}).\
get('send_anonymous_statistic', {}).get('value')
def _perform_update(self, http_method):
old_opt_in = self.single.must_send_stats()
new_opt_in = self._get_new_opt_in_status()
result = http_method()
if new_opt_in is not None and old_opt_in != new_opt_in:
self._handle_stats_opt_in(settings_data=jsonutils.loads(result))
return result
@content
def PUT(self):
return self._perform_update(
super(MasterNodeSettingsHandler, self).PUT)
@content
def PATCH(self):
result = super(MasterNodeSettingsHandler, self).PATCH()
self._handle_stats_opt_in()
return result
return self._perform_update(
super(MasterNodeSettingsHandler, self).PATCH)

View File

@ -16,6 +16,8 @@
# Common json schema types definition
from nailgun import consts
NULL = {
'type': 'null'
}
@ -161,3 +163,57 @@ RESTRICTIONS = {
"items": {"anyOf": [{"type": "string"}, _FULL_RESTRICTION,
_SHORT_RESTRICTION]}
}
UI_SETTINGS = {
"type": "object",
"required": [
"view_mode",
"filter",
"sort",
"filter_by_labels",
"sort_by_labels",
"search"
],
"additionalProperties": False,
"properties": {
"view_mode": {
"type": "string",
"description": "View mode of cluster nodes",
"enum": list(consts.NODE_VIEW_MODES),
},
"filter": {
"type": "object",
"description": ("Filters applied to node list and "
"based on node attributes"),
"properties": dict(
(key, {"type": "array"}) for key in consts.NODE_LIST_FILTERS
),
},
"sort": {
"type": "array",
"description": ("Sorters applied to node list and "
"based on node attributes"),
# TODO(@jkirnosova): describe fixed list of possible node sorters
"items": [
{"type": "object"},
],
},
"filter_by_labels": {
"type": "object",
"description": ("Filters applied to node list and "
"based on node custom labels"),
},
"sort_by_labels": {
"type": "array",
"description": ("Sorters applied to node list and "
"based on node custom labels"),
"items": [
{"type": "object"},
],
},
"search": {
"type": "string",
"description": "Search value applied to node list",
},
}
}

View File

@ -23,58 +23,6 @@ COMPONENTS_TYPES_STR = '|'.join(
COMPONENT_NAME_PATTERN = \
'^({0}):([0-9a-z_-]+:)*[0-9a-z_-]+$'.format(COMPONENTS_TYPES_STR)
CLUSTER_UI_SETTINGS = {
"type": "object",
"required": [
"view_mode",
"filter",
"sort",
"filter_by_labels",
"sort_by_labels",
"search"
],
"properties": {
"view_mode": {
"type": "string",
"description": "View mode of cluster nodes",
"enum": list(consts.NODE_VIEW_MODES),
},
"filter": {
"type": "object",
"description": ("Filters applied to node list and "
"based on node attributes"),
"properties": dict(
(key, {"type": "array"}) for key in consts.NODE_LIST_FILTERS
),
},
"sort": {
"type": "array",
"description": ("Sorters applied to node list and "
"based on node attributes"),
# TODO(@jkirnosova): describe fixed list of possible node sorters
"items": [
{"type": "object"},
],
},
"filter_by_labels": {
"type": "object",
"description": ("Filters applied to node list and "
"based on node custom labels"),
},
"sort_by_labels": {
"type": "array",
"description": ("Sorters applied to node list and "
"based on node custom labels"),
"items": [
{"type": "object"},
],
},
"search": {
"type": "string",
"description": "Search value applied to node list",
},
}
}
# TODO(@ikalnitsky): add `required` properties to all needed objects
single_schema = {
@ -93,7 +41,7 @@ single_schema = {
"type": "string",
"enum": list(consts.CLUSTER_STATUSES)
},
"ui_settings": CLUSTER_UI_SETTINGS,
"ui_settings": base_types.UI_SETTINGS,
"release_id": {"type": "number"},
"pending_release_id": base_types.NULLABLE_ID,
"replaced_deployment_info": {"type": "object"},

View File

@ -14,12 +14,19 @@
# License for the specific language governing permissions and limitations
# under the License.
from nailgun.api.v1.validators.json_schema import base_types
schema = {
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "MasterNodeSettings",
"description": "Serialized MasterNodeSettings object",
"type": "object",
"properties": {
"settings": {"type": "object"}
"settings": {
"type": "object",
"properties": {
"ui_settings": base_types.UI_SETTINGS,
},
}
}
}

View File

@ -116,15 +116,17 @@ def upgrade():
upgrade_add_baremetal_net()
upgrade_with_components()
cluster_plugin_links_upgrade()
upgrade_master_settings()
upgrade_master_node_bootstrap_settings()
upgrade_all_network_data_from_string_to_appropriate_data_type()
create_openstack_configs_table()
upgrade_master_node_ui_settings()
def downgrade():
downgrade_master_node_ui_settings()
downgrade_openstack_configs()
downgrade_all_network_data_to_string()
downgrade_master_settings()
downgrade_master_node_bootstrap_settings()
cluster_plugin_links_downgrade()
downgrade_with_components()
downgrade_add_baremetal_net()
@ -518,7 +520,7 @@ def upgrade_add_baremetal_net():
server_default='[]'))
def upgrade_master_settings():
def upgrade_master_node_bootstrap_settings():
connection = op.get_bind()
select_query = sa.sql.text("SELECT settings FROM master_node_settings")
master_settings = jsonutils.loads(
@ -537,7 +539,7 @@ def upgrade_master_settings():
connection.execute(update_query, settings=jsonutils.dumps(master_settings))
def downgrade_master_settings():
def downgrade_master_node_bootstrap_settings():
connection = op.get_bind()
select_query = sa.sql.text("SELECT settings FROM master_node_settings")
master_settings = jsonutils.loads(
@ -645,3 +647,69 @@ def ip_type_to_string(table_name, column_name, string_len):
def cluster_plugin_links_downgrade():
op.drop_table('cluster_plugin_links')
def upgrade_master_node_ui_settings():
connection = op.get_bind()
q_get_master_node_data = sa.text('''
SELECT master_node_uid, settings FROM master_node_settings
''')
q_update_master_node_settings = sa.text('''
UPDATE master_node_settings SET settings = :settings
WHERE master_node_uid = :master_node_uid
''')
master_node_data = connection.execute(q_get_master_node_data)
for master_node_uid, settings in master_node_data:
master_node_settings = jsonutils.loads(settings)
master_node_settings.update({
'ui_settings': {
'view_mode': 'standard',
'filter': {},
'sort': [{'status': 'asc'}],
'filter_by_labels': {},
'sort_by_labels': [],
'search': '',
},
})
connection.execute(q_update_master_node_settings,
master_node_uid=master_node_uid,
settings=jsonutils.dumps(master_node_settings))
alter_settings = sa.sql.text("ALTER TABLE master_node_settings "
"ALTER COLUMN settings TYPE JSON "
"USING settings::JSON")
connection.execute(alter_settings)
op.alter_column(
'master_node_settings',
'settings',
nullable=True,
existing_nullable=False,
server_default='{}'
)
def downgrade_master_node_ui_settings():
connection = op.get_bind()
op.alter_column('master_node_settings', 'settings',
nullable=True, server_default=False,
type_=sa.Text)
q_get_master_node_data = sa.text('''
SELECT master_node_uid, settings FROM master_node_settings
''')
q_update_master_node_settings = sa.text('''
UPDATE master_node_settings SET settings = :settings
WHERE master_node_uid = :master_node_uid
''')
master_node_data = connection.execute(q_get_master_node_data)
for master_node_uid, settings in master_node_data:
master_node_settings = jsonutils.loads(settings)
del master_node_settings['ui_settings']
connection.execute(q_update_master_node_settings,
master_node_uid=master_node_uid,
settings=jsonutils.dumps(master_node_settings))

View File

@ -13,11 +13,12 @@
# under the License.
from sqlalchemy import Column
from sqlalchemy.dialects.postgresql import JSON
from sqlalchemy.ext.mutable import MutableDict
from sqlalchemy import Integer
from sqlalchemy import String
from nailgun.db.sqlalchemy.models.base import Base
from nailgun.db.sqlalchemy.models.fields import JSON
class MasterNodeSettings(Base):
@ -25,4 +26,5 @@ class MasterNodeSettings(Base):
id = Column(Integer, primary_key=True)
master_node_uid = Column(String(36), nullable=False)
settings = Column(JSON, default={})
settings = Column(MutableDict.as_mutable(JSON), nullable=False, default={},
server_default='{}')

View File

@ -66,11 +66,22 @@ class MasterNodeSettings(NailgunObject):
return instance
@classmethod
def must_send_stats(cls):
def must_send_stats(cls, master_node_settings_data=None):
"""Checks if stats must be sent
Stats must be sent if user saved the choice and
sending anonymous statistics was selected.
:param master_node_settings_data: dict with master node settings.
If master_node_settings_data is None data from DB will be fetched
:return: bool
"""
try:
stat_settings = getattr(
cls.get_one(), "settings", {}
).get("statistics", {})
if master_node_settings_data is None:
settings = getattr(cls.get_one(), "settings", {})
else:
settings = master_node_settings_data.get("settings")
stat_settings = settings.get("statistics", {})
return stat_settings.get("user_choice_saved", {}).\
get("value", False) and \
stat_settings.get("send_anonymous_statistic", {}). \

View File

@ -78,6 +78,7 @@ from nailgun.middleware.connection_monitor import ConnectionMonitorMiddleware
from nailgun.middleware.keystone import NailgunFakeKeystoneAuthMiddleware
from nailgun.network.manager import NetworkManager
from nailgun.network.template import NetworkTemplate
from nailgun.utils import dict_merge
from nailgun.utils import reverse
@ -1569,6 +1570,14 @@ class BaseMasterNodeSettignsTest(BaseIntegrationTest):
master_node_settings_template = {
"settings": {
"ui_settings": {
"view_mode": "standard",
"filter": {},
"sort": [{"status": "asc"}],
"filter_by_labels": {},
"sort_by_labels": [],
"search": ""
},
"statistics": {
"send_anonymous_statistic": {
"type": "checkbox",
@ -1671,6 +1680,22 @@ class BaseMasterNodeSettignsTest(BaseIntegrationTest):
MasterNodeSettings.create(self.master_node_settings)
self.db.commit()
def set_sending_stats(self, value):
mn_settings = MasterNodeSettings.get_one()
mn_settings.settings = dict_merge(
mn_settings.settings,
{'statistics': {
'user_choice_saved': {'value': True},
'send_anonymous_statistic': {'value': value}
}})
self.db.flush()
def enable_sending_stats(self):
self.set_sending_stats(True)
def disable_sending_stats(self):
self.set_sending_stats(False)
class BaseValidatorTest(BaseTestCase):
"""JSON-schema validation policy:

View File

@ -13,7 +13,7 @@
# under the License.
import datetime
import mock
from oslo_serialization import jsonutils
from nailgun import consts
from nailgun import objects
@ -103,9 +103,7 @@ class TestActionLogs(BaseMasterNodeSettignsTest):
action_types = [al.action_type for al in action_logs]
self.assertSetEqual(set(consts.ACTION_TYPES), set(action_types))
@fake_tasks(override_state={'progress': 100,
'status': consts.TASK_STATUSES.ready})
def test_create_stats_user_logged(self):
def check_create_stats_user_logged(self, modification_api_call):
self.env.create(
nodes_kwargs=[
{'roles': ['controller'], 'pending_addition': True},
@ -115,46 +113,132 @@ class TestActionLogs(BaseMasterNodeSettignsTest):
deploy_task = self.env.launch_deployment()
self.env.wait_ready(deploy_task)
with mock.patch('nailgun.objects.MasterNodeSettings.must_send_stats',
return_value=True):
resp = self.app.patch(
reverse('MasterNodeSettingsHandler'),
headers=self.default_headers,
params='{}'
)
self.assertEqual(200, resp.status_code)
# Checking sending stats is not enabled yet by saving user choice
resp = self.app.get(
reverse('MasterNodeSettingsHandler'),
headers=self.default_headers)
data = resp.json_body
self.assertFalse(data['settings']['statistics']
['user_choice_saved']['value'])
self.assertTrue(data['settings']['statistics']
['send_anonymous_statistic']['value'])
task = objects.TaskCollection.filter_by(
None, name=consts.TASK_NAMES.create_stats_user).first()
# Enabling stats
stat_settings = data['settings']['statistics']
stat_settings['user_choice_saved']['value'] = True
stat_settings['send_anonymous_statistic']['value'] = True
resp = modification_api_call(
reverse('MasterNodeSettingsHandler'),
headers=self.default_headers,
params=jsonutils.dumps(data)
)
self.assertEqual(200, resp.status_code)
tasks = objects.TaskCollection.filter_by(
None, name=consts.TASK_NAMES.create_stats_user).all()
tasks_count = len(tasks)
task = tasks[-1]
action_log = objects.ActionLogCollection.filter_by(
None, task_uuid=task.uuid)
self.assertIsNotNone(action_log)
# Checking settings modified
resp = self.app.get(
reverse('MasterNodeSettingsHandler'),
headers=self.default_headers)
new_data = resp.json_body
self.assertTrue(new_data['settings']['statistics']
['user_choice_saved']['value'])
self.assertTrue(new_data['settings']['statistics']
['send_anonymous_statistic']['value'])
# Checking the second call doesn't produce task
resp = modification_api_call(
reverse('MasterNodeSettingsHandler'),
headers=self.default_headers,
params=jsonutils.dumps(data)
)
self.assertEqual(200, resp.status_code)
duplicate_call_tasks_count = objects.TaskCollection.filter_by(
None, name=consts.TASK_NAMES.create_stats_user).count()
self.assertEquals(tasks_count, duplicate_call_tasks_count)
@fake_tasks(override_state={'progress': 100,
'status': consts.TASK_STATUSES.ready})
def test_remove_stats_user_logged(self):
def test_create_stats_user_logged_put(self):
self.check_create_stats_user_logged(self.app.put)
@fake_tasks(override_state={'progress': 100,
'status': consts.TASK_STATUSES.ready})
def test_create_stats_user_logged_patch(self):
self.check_create_stats_user_logged(self.app.patch)
def check_remove_stats_user_logged(self, modification_api_call):
self.env.create(
nodes_kwargs=[
{'roles': ['controller'], 'pending_addition': True},
]
)
with mock.patch('nailgun.objects.MasterNodeSettings.must_send_stats',
return_value=True):
deploy_task = self.env.launch_deployment()
self.env.wait_ready(deploy_task)
deploy_task = self.env.launch_deployment()
self.env.wait_ready(deploy_task)
with mock.patch('nailgun.objects.MasterNodeSettings.must_send_stats',
return_value=False):
resp = self.app.patch(
reverse('MasterNodeSettingsHandler'),
headers=self.default_headers,
params='{}'
)
self.assertEqual(200, resp.status_code)
self.enable_sending_stats()
task = objects.TaskCollection.filter_by(
None, name=consts.TASK_NAMES.remove_stats_user).first()
resp = self.app.get(
reverse('MasterNodeSettingsHandler'),
headers=self.default_headers)
data = resp.json_body
self.assertTrue(data['settings']['statistics']
['user_choice_saved']['value'])
self.assertTrue(data['settings']['statistics']
['send_anonymous_statistic']['value'])
# Disabling stats
stat_settings = data['settings']['statistics']
stat_settings['send_anonymous_statistic']['value'] = False
resp = modification_api_call(
reverse('MasterNodeSettingsHandler'),
headers=self.default_headers,
params=jsonutils.dumps(data)
)
self.assertEqual(200, resp.status_code)
tasks = objects.TaskCollection.filter_by(
None, name=consts.TASK_NAMES.remove_stats_user).all()
tasks_count = len(tasks)
task = tasks[-1]
action_log = objects.ActionLogCollection.filter_by(
None, task_uuid=task.uuid)
self.assertIsNotNone(action_log)
# Checking settings modified
resp = self.app.get(
reverse('MasterNodeSettingsHandler'),
headers=self.default_headers)
new_data = resp.json_body
self.assertTrue(new_data['settings']['statistics']
['user_choice_saved']['value'])
self.assertFalse(new_data['settings']['statistics']
['send_anonymous_statistic']['value'])
# Checking the second call doesn't produce task
resp = modification_api_call(
reverse('MasterNodeSettingsHandler'),
headers=self.default_headers,
params=jsonutils.dumps(data)
)
self.assertEqual(200, resp.status_code)
duplicate_call_tasks_count = objects.TaskCollection.filter_by(
None, name=consts.TASK_NAMES.remove_stats_user).count()
self.assertEquals(tasks_count, duplicate_call_tasks_count)
@fake_tasks(override_state={'progress': 100,
'status': consts.TASK_STATUSES.ready})
def test_remove_stats_user_logged_put(self):
self.check_remove_stats_user_logged(self.app.put)
@fake_tasks(override_state={'progress': 100,
'status': consts.TASK_STATUSES.ready})
def test_remove_stats_user_logged_patch(self):
self.check_remove_stats_user_logged(self.app.patch)

View File

@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
# Copyright 2014 Mirantis, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
@ -13,6 +14,7 @@
# under the License.
import copy
import functools
import mock
from oslo_serialization import jsonutils
@ -96,7 +98,16 @@ class TestMasterNodeSettingsHandler(BaseMasterNodeSettignsTest):
def test_validate_ok(self):
data = {
"settings": {},
"settings": {
"ui_settings": {
"view_mode": "standard",
"filter": {},
"sort": [{"status": "asc"}],
"filter_by_labels": {},
"sort_by_labels": [],
"search": ""
}
},
}
resp = self.app.put(
@ -290,3 +301,88 @@ class TestMasterNodeSettingsHandler(BaseMasterNodeSettignsTest):
{'contact_info_provided': False})
self.assertIsNone(
InstallationInfo().get_installation_info()['master_node_uid'])
def get_current_settings(self):
resp = self.app.get(
reverse("MasterNodeSettingsHandler"),
headers=self.default_headers)
self.assertEqual(200, resp.status_code)
return resp.json_body
def check_task_created_only_on_new_opt_in(self, handler_method):
def get_settings_value(data, setting_name):
return data['settings']['statistics'][setting_name]['value']
def set_settings_value(data, setting_name, value):
data['settings']['statistics'][setting_name]['value'] = value
with mock.patch('nailgun.api.v1.handlers.master_node_settings.'
'MasterNodeSettingsHandler._handle_stats_opt_in'
) as task_creator:
# Checking called on enabling sending
data = self.get_current_settings()
self.assertFalse(get_settings_value(data, 'user_choice_saved'))
set_settings_value(data, 'user_choice_saved', True)
resp = handler_method(params=jsonutils.dumps(data))
self.assertEqual(200, resp.status_code)
self.assertEqual(1, task_creator.call_count)
# Checking not called on same value
data = self.get_current_settings()
self.assertTrue(get_settings_value(data, 'user_choice_saved'))
resp = handler_method(params=jsonutils.dumps(data))
self.assertEqual(200, resp.status_code)
self.assertEqual(1, task_creator.call_count)
# Checking called on another opt in value
data = self.get_current_settings()
self.assertTrue(get_settings_value(data, 'user_choice_saved'))
opt_in = get_settings_value(data, 'send_anonymous_statistic')
set_settings_value(data, 'send_anonymous_statistic', not opt_in)
resp = handler_method(params=jsonutils.dumps(data))
self.assertEqual(200, resp.status_code)
self.assertEqual(2, task_creator.call_count)
def test_task_created_only_on_put_new_opt_in(self):
handler_method = functools.partial(
self.app.put, reverse("MasterNodeSettingsHandler"),
headers=self.default_headers)
self.check_task_created_only_on_new_opt_in(handler_method)
def test_task_created_only_on_patch_new_opt_in(self):
handler_method = functools.partial(
self.app.patch, reverse("MasterNodeSettingsHandler"),
headers=self.default_headers)
self.check_task_created_only_on_new_opt_in(handler_method)
def test_unicode_master_node_settings(self):
data = self.get_current_settings()
# emulate user enabled contact info sending to support team
data["settings"]["statistics"]["user_choice_saved"]["value"] = True
data["settings"]["statistics"]["send_user_info"]["value"] = \
True
name = u"Фёдор Я"
email = "u@e.mail"
company = u"Компания"
data["settings"]["statistics"]["name"]["value"] = name
data["settings"]["statistics"]["email"]["value"] = email
data["settings"]["statistics"]["company"]["value"] = company
resp = self.app.put(
reverse("MasterNodeSettingsHandler"),
headers=self.default_headers,
params=jsonutils.dumps(data)
)
self.assertEqual(200, resp.status_code)
self.assertDictEqual(
InstallationInfo().get_installation_info()['user_information'],
{
'contact_info_provided': True,
'name': name,
'email': email,
'company': company
}
)

View File

@ -40,15 +40,19 @@ class TestStatsUserTaskManagers(BaseMasterNodeSettignsTest):
deploy_task = self.env.launch_deployment()
self.env.wait_ready(deploy_task)
self.assertFalse(objects.MasterNodeSettings.must_send_stats())
with mock.patch('nailgun.objects.MasterNodeSettings.must_send_stats',
return_value=True):
resp = self.app.patch(
reverse('MasterNodeSettingsHandler'),
headers=self.default_headers,
params=jsonutils.dumps({})
)
self.assertEqual(200, resp.status_code)
data = {'settings': {'statistics': {
'user_choice_saved': {'value': True},
'send_anonymous_statistic': {'value': True}
}}}
resp = self.app.patch(
reverse('MasterNodeSettingsHandler'),
headers=self.default_headers,
params=jsonutils.dumps(data)
)
self.assertEqual(200, resp.status_code)
task = objects.TaskCollection.filter_by(
None, name=consts.TASK_NAMES.create_stats_user).first()
@ -77,23 +81,31 @@ class TestStatsUserTaskManagers(BaseMasterNodeSettignsTest):
task_count_before = objects.TaskCollection.filter_by(
None, name=task_name).count()
with mock.patch('nailgun.objects.MasterNodeSettings.'
'must_send_stats', return_value=must_send_stats):
with mock.patch('nailgun.task.fake.settings.'
'FAKE_TASKS_TICK_INTERVAL', 10):
resp = self.app.patch(
reverse('MasterNodeSettingsHandler'),
headers=self.default_headers,
params='{}'
)
self.assertEqual(200, resp.status_code)
data = {'settings': {'statistics': {
'user_choice_saved': {'value': True},
'send_anonymous_statistic': {'value': must_send_stats}
}}}
resp = self.app.patch(
reverse('MasterNodeSettingsHandler'),
headers=self.default_headers,
params='{}'
)
self.assertEqual(200, resp.status_code)
if must_send_stats:
self.disable_sending_stats()
else:
self.enable_sending_stats()
with mock.patch('nailgun.task.fake.settings.'
'FAKE_TASKS_TICK_INTERVAL', 10):
resp = self.app.patch(
reverse('MasterNodeSettingsHandler'),
headers=self.default_headers,
params=jsonutils.dumps(data)
)
self.assertEqual(200, resp.status_code)
resp = self.app.patch(
reverse('MasterNodeSettingsHandler'),
headers=self.default_headers,
params=jsonutils.dumps(data)
)
self.assertEqual(200, resp.status_code)
task_count = objects.TaskCollection.filter_by(
None, name=task_name).count()
@ -156,17 +168,21 @@ class TestStatsUserTaskManagers(BaseMasterNodeSettignsTest):
self.assertFalse(executer.called)
def test_create_stats_user_called(self):
with mock.patch('nailgun.objects.MasterNodeSettings.must_send_stats',
return_value=True):
with mock.patch('nailgun.task.manager.CreateStatsUserTaskManager.'
'execute') as executer:
resp = self.app.patch(
reverse('MasterNodeSettingsHandler'),
headers=self.default_headers,
params=jsonutils.dumps({})
)
self.assertEqual(200, resp.status_code)
self.assertTrue(executer.called)
self.assertFalse(objects.MasterNodeSettings.must_send_stats())
data = {'settings': {'statistics': {
'user_choice_saved': {'value': True},
'send_anonymous_statistic': {'value': True}
}}}
with mock.patch('nailgun.task.manager.CreateStatsUserTaskManager.'
'execute') as executer:
resp = self.app.patch(
reverse('MasterNodeSettingsHandler'),
headers=self.default_headers,
params=jsonutils.dumps(data)
)
self.assertEqual(200, resp.status_code)
self.assertTrue(executer.called)
@fake_tasks(override_state={'progress': 100,
'status': consts.TASK_STATUSES.ready})
@ -178,20 +194,22 @@ class TestStatsUserTaskManagers(BaseMasterNodeSettignsTest):
{'roles': ['controller'], 'pending_addition': True},
]
)
self.enable_sending_stats()
self.assertTrue(objects.MasterNodeSettings.must_send_stats())
deploy_task = self.env.launch_deployment()
self.env.wait_ready(deploy_task)
with mock.patch('nailgun.objects.MasterNodeSettings.must_send_stats',
return_value=True):
deploy_task = self.env.launch_deployment()
self.env.wait_ready(deploy_task)
data = {'settings': {'statistics': {
'user_choice_saved': {'value': True},
'send_anonymous_statistic': {'value': False}
}}}
with mock.patch('nailgun.objects.MasterNodeSettings.must_send_stats',
return_value=False):
resp = self.app.patch(
reverse('MasterNodeSettingsHandler'),
headers=self.default_headers,
params=jsonutils.dumps({})
)
self.assertEqual(200, resp.status_code)
resp = self.app.patch(
reverse('MasterNodeSettingsHandler'),
headers=self.default_headers,
params=jsonutils.dumps(data)
)
self.assertEqual(200, resp.status_code)
task = objects.TaskCollection.filter_by(
None, name=consts.TASK_NAMES.remove_stats_user).first()
@ -201,24 +219,30 @@ class TestStatsUserTaskManagers(BaseMasterNodeSettignsTest):
with mock.patch('nailgun.objects.MasterNodeSettings.must_send_stats',
return_value=True):
with mock.patch('nailgun.task.manager.RemoveStatsUserTaskManager.'
'execute') as executer:
'execute') as executor:
resp = self.app.patch(
reverse('MasterNodeSettingsHandler'),
headers=self.default_headers,
params=jsonutils.dumps({})
)
self.assertEqual(200, resp.status_code)
self.assertFalse(executer.called)
self.assertFalse(executor.called)
def test_remove_stats_user_called(self):
with mock.patch('nailgun.objects.MasterNodeSettings.must_send_stats',
return_value=False):
with mock.patch('nailgun.task.manager.RemoveStatsUserTaskManager.'
'execute') as executer:
resp = self.app.patch(
reverse('MasterNodeSettingsHandler'),
headers=self.default_headers,
params=jsonutils.dumps({})
)
self.assertEqual(200, resp.status_code)
self.assertTrue(executer.called)
self.enable_sending_stats()
self.assertTrue(objects.MasterNodeSettings.must_send_stats())
data = {'settings': {'statistics': {
'user_choice_saved': {'value': True},
'send_anonymous_statistic': {'value': False}
}}}
with mock.patch('nailgun.task.manager.RemoveStatsUserTaskManager.'
'execute') as executor:
resp = self.app.patch(
reverse('MasterNodeSettingsHandler'),
headers=self.default_headers,
params=jsonutils.dumps(data)
)
self.assertEqual(200, resp.status_code)
self.assertTrue(executor.called)

View File

@ -33,13 +33,29 @@ _prepare_revision = '1e50a4903910'
_test_revision = '43b2cb64dae6'
master_node_settings_before_migration = None
def setup_module():
dropdb()
alembic.command.upgrade(ALEMBIC_CONFIG, _prepare_revision)
prepare()
global master_node_settings_before_migration
master_node_settings_before_migration = jsonutils.loads(
get_master_node_settings())
alembic.command.upgrade(ALEMBIC_CONFIG, _test_revision)
def get_master_node_settings():
meta = base.reflect_db_metadata()
master_node_settings_table = meta.tables['master_node_settings']
settings = db.execute(sa.select(
[master_node_settings_table.c.settings])).scalar()
db().commit()
return settings
def prepare():
meta = base.reflect_db_metadata()
@ -458,11 +474,10 @@ class TestComponentsMigration(base.BaseAlembicMigrationTest):
self.assertEqual(jsonutils.loads(db_value), column_values[idx][1])
class TestMasterSettingsMigration(base.BaseAlembicMigrationTest):
class TestMasterNodeSettingsMigration(base.BaseAlembicMigrationTest):
def test_bootstrap_field_exists_and_filled(self):
result = db.execute(
sa.select([self.meta.tables['master_node_settings'].c.settings]))
settings = get_master_node_settings()
bootstrap_settings = {
"error": {
"type": "hidden",
@ -472,7 +487,26 @@ class TestMasterSettingsMigration(base.BaseAlembicMigrationTest):
}
self.assertEqual(
bootstrap_settings,
jsonutils.loads(result.scalar())['bootstrap']
settings['bootstrap']
)
def test_ui_settings_field_exists_and_has_default_value(self):
settings = get_master_node_settings()
ui_settings = settings['ui_settings']
self.assertItemsEqual(ui_settings['view_mode'], 'standard')
self.assertItemsEqual(ui_settings['filter'], {})
self.assertItemsEqual(ui_settings['sort'], [{'status': 'asc'}])
self.assertItemsEqual(ui_settings['filter_by_labels'], {})
self.assertItemsEqual(ui_settings['sort_by_labels'], [])
self.assertItemsEqual(ui_settings['search'], '')
def test_master_node_settings_old_data_not_modified(self):
settings = get_master_node_settings()
settings.pop('bootstrap')
settings.pop('ui_settings')
self.assertDictEqual(
master_node_settings_before_migration,
settings
)