Permalink
Browse files

HUE-486. Enable renaming of top-level configs, and add a path for per…

…forming search/replace config upgrades
  • Loading branch information...
1 parent 3441c82 commit 502f42f02ce6526153e759fb41c7f5419a6819af Jon Natkins committed Mar 1, 2011
@@ -91,21 +91,34 @@ def __init__(self, module):
module. Static directories, settings, urls, etc.
will be found based on that module.
"""
+
+ # For clarification purposes, all of these different names need a
+ # bit of explanation. The name is the actual name of the application.
+ # The display name is used by dump_config, and will either be the
+ # app name or the config key, if the config key has been defined in the
+ # app's settings. Mostly, it's around for consistency's sake.
+ # The nice name is just a more formal name, i.e. useradmin might
+ # have a nice name of User Administration Tool, or something
+ # similarly flowery.
self.module = module
self.name = module.__name__
+ self.display_name = module.__name__
# Set up paths
module_root = os.path.dirname(module.__file__)
self.root_dir = os.path.join(module_root, "..", "..")
# Load application settings
self._load_settings_module(module.__name__ + ".settings")
-
+
if hasattr(self.settings, "NICE_NAME"):
self.nice_name = self.settings.NICE_NAME
else:
self.nice_name = self.name
+ if self.config_key is not None:
+ self.display_name = self.config_key
+
# Look for static directory in two places:
new_style, old_style = [ os.path.abspath(p) for p in [
os.path.join(module_root, "static"),
@@ -131,13 +144,15 @@ def _load_settings_module(self, module_name):
s = _import_module_or_none(module_name)
if s is not None:
self.django_apps = getattr(s, 'DJANGO_APPS', [])
+ self.config_key = getattr(s, 'CONFIG_KEY', None)
self.depender_yamls = \
[self._resolve_appdir_path(p) for p in getattr(s, 'DEPENDER_PACKAGE_YMLS', [])]
self.depender_jsons = \
[(depname, self._resolve_appdir_path(p))
for depname, p in getattr(s, 'DEPENDER_SCRIPTS_JSON', [])]
else:
self.django_apps = []
+ self.config_key = None
self.depender_yamls = []
self.depender_jsons = []
@@ -522,7 +522,7 @@ def _bind_module_members(module, data, section):
return members
-def bind_module_config(mod, conf_data):
+def bind_module_config(mod, conf_data, config_key):
"""Binds the configuration for the module to the given data.
conf_data is a dict-like structure in which the configuration data
@@ -533,6 +533,9 @@ def bind_module_config(mod, conf_data):
- if the module has a CONFIGURATION_SECTION attribute, that attribute
should be a string, and determines the section name.
+ config_key is the key that should map to the configuration.
+ It's used to allow renaming of configurations.
+
For example, for the module "hello.world.conf", type(conf_data['hello.world'])
should be dict-like and contain the configuration for the hello.world
module.
@@ -548,7 +551,12 @@ def bind_module_config(mod, conf_data):
else:
section = mod.__name__
- bind_data = conf_data.get(section, {})
+ if config_key is None:
+ bind_data = conf_data.get(section, {})
+ else:
+ section = config_key
+ bind_data = conf_data.get(config_key, {})
+
members = _bind_module_members(mod, bind_data, section)
return ConfigSection(section,
members=members,
@@ -566,7 +574,7 @@ def initialize(modules, config_dir):
conf_data = load_confs(_configs_from_dir(config_dir))
sections = {}
for module in modules:
- section = bind_module_config(module, conf_data)
+ section = bind_module_config(module['module'], conf_data, module['config_key'])
sections[section.key] = section
GLOBAL_HELP = "(root of all configuration)"
@@ -0,0 +1,88 @@
+#!/usr/bin/env python
+# Licensed to Cloudera, Inc. under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. Cloudera, Inc. licenses this file
+# to you 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.
+"""
+Upgrades a configuration from a mapping file.
+
+Mapping files have a series of search/replace instructions in the form
+s/<old_value>/<new_value>/
+This will rewrite the configurations if any changes are required.
+"""
+import logging
+
+import os, glob, string
+import desktop.conf
+import desktop.log
+from optparse import make_option
+from desktop.lib.paths import get_desktop_root
+from django.core.management.base import BaseCommand, CommandError
+
+LOG = logging.getLogger(__name__)
+
+class Command(BaseCommand):
+ args = ''
+ help = 'Upgrades the Hue configuration with a mapping file.'
+
+ option_list = BaseCommand.option_list + (
+ make_option('--mapping_file', help='Location of the mapping file.'),
+ )
+
+ """Upgrades a configuration."""
+ def handle(self, *args, **options):
+ required = ("mapping_file",)
+ for r in required:
+ if not options.get(r):
+ raise CommandError("--%s is required." % r)
+
+ # Pull out all the mappings
+ mapping_file = options["mapping_file"]
+ mapping_handle = open(mapping_file, 'r')
+ mappings = []
+ for mapping in mapping_handle:
+ map_parts = mapping.strip().lstrip('s/')
+ map_parts = map_parts.rstrip('/')
+ map_parts = map_parts.split('/')
+ if len(map_parts) != 2:
+ raise CommandError("Invalid mapping %s in %s" % (mapping.strip(), mapping_file,))
+ mappings.append(map_parts)
+
+ config_dir = os.getenv("HUE_CONF_DIR", get_desktop_root("conf"))
+ for conf_file in glob.glob(os.path.join(config_dir, '*.ini')):
+ LOG.info("Upgrading %s" % conf_file)
+ conf_handle = open(conf_file, 'r')
+ data = []
+ for line in conf_handle:
+ # Pull apart any variables so we don't overwrite config settings
+ data.append(line.split('=', 1))
+
+ # Iterate over mappings to perform
+ for line in data:
+ for mapping in mappings:
+ old_value = mapping[0]
+ new_value = mapping[1]
+
+ if old_value in line[0]:
+ LOG.info("Replacing %s with %s in line %s" %
+ (old_value, new_value, '='.join(line),))
+ line[0] = line[0].replace(old_value, new_value)
+
+
+ # Rewrite file with replacements made
+ conf_handle.close()
+ conf_handle = open(conf_file, 'w')
+ data_to_write = ''.join([ '='.join(split) for split in data ])
+ conf_handle.write(data_to_write)
+
@@ -161,7 +161,7 @@
# Libraries are loaded and configured before the apps
appmanager.load_libs()
-_lib_conf_modules = filter(None, [app.conf for app in appmanager.DESKTOP_LIBS])
+_lib_conf_modules = [dict(module=app.conf, config_key=None) for app in appmanager.DESKTOP_LIBS if app.conf is not None]
appmanager.load_apps()
for app in appmanager.DESKTOP_APPS:
@@ -170,8 +170,8 @@
logging.debug("Installed Django modules: %s" % ",".join(map(str, appmanager.DESKTOP_MODULES)))
# Load app configuration
-_app_conf_modules = filter(None, [app.conf for app in appmanager.DESKTOP_APPS])
-_app_conf_modules.append(desktop.conf)
+_app_conf_modules = [dict(module=app.conf, config_key=app.config_key) for app in appmanager.DESKTOP_APPS if app.conf is not None]
+_app_conf_modules.append(dict(module=desktop.conf, config_key=None))
conf.initialize(_lib_conf_modules, _config_dir)
conf.initialize(_app_conf_modules, _config_dir)
@@ -28,7 +28,7 @@ from desktop.lib.conf import BoundContainer, is_anonymous
<ul>
% for app in sorted(apps, key=lambda app: app.name.lower()):
% if hasattr(app, "urls_imported") and app.urls_imported:
- <li><a href="/${app.name}/">${app.name}</a></li>
+ <li><a href="/${app.name}/">${app.display_name}</a></li>
% else:
<li>${app.name}</li>
% endif

0 comments on commit 502f42f

Please sign in to comment.