Smarter upgrades of node type definitions

- No changes are applied unless the new --go CLI arg is used.
- Differences to node types are actually shown.
- Dynamic form definitions are kept.
This commit is contained in:
2018-03-27 11:48:40 +02:00
parent dee0b18429
commit d24715a224
3 changed files with 53 additions and 28 deletions

View File

@@ -158,7 +158,12 @@ class MetaFalsey(type):
return False
class DoesNotExist(object, metaclass=MetaFalsey):
class DoesNotExistMeta(MetaFalsey):
def __repr__(cls) -> str:
return 'DoesNotExist'
class DoesNotExist(object, metaclass=DoesNotExistMeta):
"""Returned as value by doc_diff if a value does not exist."""

View File

@@ -412,13 +412,17 @@ def expire_all_project_links(project_uuid):
@manager_maintenance.option('-m', '--missing', dest='missing',
action='store_true', default=False,
help='Add missing node types. Note that this may add unwanted ones.')
def replace_pillar_node_type_schemas(proj_url=None, all_projects=False, missing=False):
@manager_maintenance.option('-g', '--go', dest='gogogo',
action='store_true', default=False,
help='Actually go and perform the changes, without this just '
'shows differences.')
def replace_pillar_node_type_schemas(project_url=None, all_projects=False, missing=False, go=False):
"""Replaces the project's node type schemas with the standard Pillar ones.
Non-standard node types are left alone.
"""
if bool(proj_url) == all_projects:
if bool(project_url) == all_projects:
log.error('Use either --project or --all.')
return 1
@@ -426,57 +430,73 @@ def replace_pillar_node_type_schemas(proj_url=None, all_projects=False, missing=
force_cli_user()
from pillar.api.node_types import PILLAR_NAMED_NODE_TYPES
from pillar.api.utils import remove_private_keys
from pillar.api.utils import remove_private_keys, doc_diff
projects_collection = current_app.db()['projects']
will_would = 'Will' if go else 'Would'
def handle_project(project):
log.info('Handling project %s', project['url'])
is_public_proj = not project.get('is_private', True)
def handle_project(proj):
orig_proj = copy.deepcopy(proj)
proj_id = proj['_id']
if 'url' not in proj:
log.warning('Project %s has no URL!', proj_id)
proj_url = proj.get('url', f'-no URL id {proj_id}')
log.debug('Handling project %s', proj_url)
for proj_nt in project['node_types']:
for proj_nt in proj['node_types']:
nt_name = proj_nt['name']
try:
pillar_nt = PILLAR_NAMED_NODE_TYPES[nt_name]
except KeyError:
log.info(' - skipping non-standard node type "%s"', nt_name)
log.debug(' - skipping non-standard node type "%s"', nt_name)
continue
log.info(' - replacing schema on node type "%s"', nt_name)
log.debug(' - replacing schema on node type "%s"', nt_name)
# This leaves node type keys intact that aren't in Pillar's node_type_xxx definitions,
# such as permissions.
# such as permissions. It also keeps form schemas as-is.
pillar_nt.pop('form_schema', None)
proj_nt.update(copy.deepcopy(pillar_nt))
# On our own public projects we want to be able to set license stuff.
if is_public_proj:
proj_nt['form_schema'].pop('license_type', None)
proj_nt['form_schema'].pop('license_notes', None)
# Find new node types that aren't in the project yet.
if missing:
project_ntnames = set(nt['name'] for nt in project['node_types'])
project_ntnames = set(nt['name'] for nt in proj['node_types'])
for nt_name in set(PILLAR_NAMED_NODE_TYPES.keys()) - project_ntnames:
log.info(' - Adding node type "%s"', nt_name)
pillar_nt = PILLAR_NAMED_NODE_TYPES[nt_name]
project['node_types'].append(copy.deepcopy(pillar_nt))
proj['node_types'].append(copy.deepcopy(pillar_nt))
# Use Eve to PUT, so we have schema checking.
db_proj = remove_private_keys(project)
r, _, _, status = current_app.put_internal('projects', db_proj, _id=project['_id'])
if status != 200:
log.error('Error %i storing altered project %s %s', status, project['_id'], r)
raise SystemExit('Error storing project, see log.')
log.info('Project saved succesfully.')
proj_header_shown = False
for key, val1, val2 in doc_diff(orig_proj, proj, falsey_is_equal=False):
if not proj_header_shown:
if proj.get('_deleted', False):
deleted = ' (deleted)'
else:
deleted = ''
log.info('%s change project %s%s', will_would, proj_url, deleted)
proj_header_shown = True
log.info(' %30r: %r%r', key, val1, val2)
if go:
# Use Eve to PUT, so we have schema checking.
db_proj = remove_private_keys(proj)
r, _, _, status = current_app.put_internal('projects', db_proj, _id=proj['_id'])
if status != 200:
log.error('Error %i storing altered project %s %s', status, proj['_id'], r)
raise SystemExit('Error storing project, see log.')
log.info('Project saved succesfully.')
if not go:
log.info('Not changing anything, use --go to actually go and change things.')
if all_projects:
for project in projects_collection.find():
handle_project(project)
return
project = projects_collection.find_one({'url': proj_url})
project = projects_collection.find_one({'url': project_url})
if not project:
log.error('Project url=%s not found', proj_url)
log.error('Project url=%s not found', project_url)
return 3
handle_project(project)