diff --git a/hooks/glance_relations.py b/hooks/glance_relations.py index f151c3c4..97cf1ec3 100755 --- a/hooks/glance_relations.py +++ b/hooks/glance_relations.py @@ -18,14 +18,13 @@ from glance_utils import ( SERVICES, CHARM, GLANCE_REGISTRY_CONF, - GLANCE_REGISTRY_PASTE_INI, GLANCE_API_CONF, - GLANCE_API_PASTE_INI, HAPROXY_CONF, ceph_config_file, setup_ipv6, swift_temp_url_key, assess_status, + reinstall_paste_ini, ) from charmhelpers.core.hookenv import ( config, @@ -318,9 +317,6 @@ def keystone_changed(): CONFIGS.write(GLANCE_API_CONF) CONFIGS.write(GLANCE_REGISTRY_CONF) - CONFIGS.write(GLANCE_API_PASTE_INI) - CONFIGS.write(GLANCE_REGISTRY_PASTE_INI) - # Configure any object-store / swift relations now that we have an # identity-service if relation_ids('object-store'): @@ -391,6 +387,7 @@ def cluster_changed(): @restart_on_change(restart_map(), stopstart=True) def upgrade_charm(): apt_install(filter_installed_packages(determine_packages()), fatal=True) + reinstall_paste_ini() configure_https() update_nrpe_config() CONFIGS.write_all() diff --git a/hooks/glance_utils.py b/hooks/glance_utils.py index eb5014c4..1b693a73 100644 --- a/hooks/glance_utils.py +++ b/hooks/glance_utils.py @@ -113,9 +113,11 @@ CHARM = "glance" GLANCE_CONF_DIR = "/etc/glance" GLANCE_REGISTRY_CONF = "%s/glance-registry.conf" % GLANCE_CONF_DIR -GLANCE_REGISTRY_PASTE_INI = "%s/glance-registry-paste.ini" % GLANCE_CONF_DIR GLANCE_API_CONF = "%s/glance-api.conf" % GLANCE_CONF_DIR -GLANCE_API_PASTE_INI = "%s/glance-api-paste.ini" % GLANCE_CONF_DIR +GLANCE_REGISTRY_PASTE = os.path.join(GLANCE_CONF_DIR, + 'glance-registry-paste.ini') +GLANCE_API_PASTE = os.path.join(GLANCE_CONF_DIR, + 'glance-api-paste.ini') CEPH_CONF = "/etc/ceph/ceph.conf" CHARM_CEPH_CONF = '/var/lib/charm/{}/ceph.conf' @@ -175,14 +177,6 @@ CONFIG_FILES = OrderedDict([ template_flag='api_config_flags')], 'services': ['glance-api'] }), - (GLANCE_API_PASTE_INI, { - 'hook_contexts': [context.IdentityServiceContext()], - 'services': ['glance-api'] - }), - (GLANCE_REGISTRY_PASTE_INI, { - 'hook_contexts': [context.IdentityServiceContext()], - 'services': ['glance-registry'] - }), (ceph_config_file(), { 'hook_contexts': [context.CephContext()], 'services': ['glance-api', 'glance-registry'] @@ -213,8 +207,6 @@ def register_configs(): confs = [GLANCE_REGISTRY_CONF, GLANCE_API_CONF, - GLANCE_API_PASTE_INI, - GLANCE_REGISTRY_PASTE_INI, HAPROXY_CONF] if relation_ids('ceph'): @@ -537,3 +529,33 @@ def assess_status(configs): # set the status according to the current state of the contexts set_os_workload_status( configs, REQUIRED_INTERFACES, charm_func=check_optional_relations) + + +PASTE_INI_MARKER = 'paste-ini-marker' +REINSTALL_OPTIONS = [ + '--reinstall', + '--option=Dpkg::Options::=--force-confmiss' +] + + +def reinstall_paste_ini(): + ''' + Re-install glance-{api,registry}-paste.ini file from packages + + Existing glance-{api,registry}-paste.ini file will be removed + and the original files provided by the packages will be + re-installed. + + This will only ever be performed once per unit. + ''' + db = kv() + if not db.get(PASTE_INI_MARKER): + for paste_file in [GLANCE_REGISTRY_PASTE, + GLANCE_API_PASTE]: + if os.path.exists(paste_file): + os.remove(paste_file) + apt_install(packages=['glance-api', 'glance-registry'], + options=REINSTALL_OPTIONS, + fatal=True) + db.set(PASTE_INI_MARKER, True) + db.flush() diff --git a/hooks/upgrade-charm b/hooks/upgrade-charm new file mode 120000 index 00000000..e955ca43 --- /dev/null +++ b/hooks/upgrade-charm @@ -0,0 +1 @@ +glance_relations.py \ No newline at end of file diff --git a/templates/icehouse/glance-api-paste.ini b/templates/icehouse/glance-api-paste.ini deleted file mode 100644 index 4f8f6599..00000000 --- a/templates/icehouse/glance-api-paste.ini +++ /dev/null @@ -1,72 +0,0 @@ -# Use this pipeline for no auth or image caching - DEFAULT -[pipeline:glance-api] -pipeline = versionnegotiation unauthenticated-context rootapp - -# Use this pipeline for image caching and no auth -[pipeline:glance-api-caching] -pipeline = versionnegotiation unauthenticated-context cache rootapp - -# Use this pipeline for caching w/ management interface but no auth -[pipeline:glance-api-cachemanagement] -pipeline = versionnegotiation unauthenticated-context cache cachemanage rootapp - -# Use this pipeline for keystone auth -[pipeline:glance-api-keystone] -pipeline = versionnegotiation authtoken context rootapp - -# Use this pipeline for keystone auth with image caching -[pipeline:glance-api-keystone+caching] -pipeline = versionnegotiation authtoken context cache rootapp - -# Use this pipeline for keystone auth with caching and cache management -[pipeline:glance-api-keystone+cachemanagement] -pipeline = versionnegotiation authtoken context cache cachemanage rootapp - -# Use this pipeline for authZ only. This means that the registry will treat a -# user as authenticated without making requests to keystone to reauthenticate -# the user. -[pipeline:glance-api-trusted-auth] -pipeline = versionnegotiation context rootapp - -# Use this pipeline for authZ only. This means that the registry will treat a -# user as authenticated without making requests to keystone to reauthenticate -# the user and uses cache management -[pipeline:glance-api-trusted-auth+cachemanagement] -pipeline = versionnegotiation context cache cachemanage rootapp - -[composite:rootapp] -paste.composite_factory = glance.api:root_app_factory -/: apiversions -/v1: apiv1app -/v2: apiv2app - -[app:apiversions] -paste.app_factory = glance.api.versions:create_resource - -[app:apiv1app] -paste.app_factory = glance.api.v1.router:API.factory - -[app:apiv2app] -paste.app_factory = glance.api.v2.router:API.factory - -[filter:versionnegotiation] -paste.filter_factory = glance.api.middleware.version_negotiation:VersionNegotiationFilter.factory - -[filter:cache] -paste.filter_factory = glance.api.middleware.cache:CacheFilter.factory - -[filter:cachemanage] -paste.filter_factory = glance.api.middleware.cache_manage:CacheManageFilter.factory - -[filter:context] -paste.filter_factory = glance.api.middleware.context:ContextMiddleware.factory - -[filter:unauthenticated-context] -paste.filter_factory = glance.api.middleware.context:UnauthenticatedContextMiddleware.factory - -[filter:authtoken] -paste.filter_factory = keystoneclient.middleware.auth_token:filter_factory -delay_auth_decision = true - -[filter:gzip] -paste.filter_factory = glance.api.middleware.gzip:GzipMiddleware.factory diff --git a/templates/icehouse/glance-registry-paste.ini b/templates/icehouse/glance-registry-paste.ini deleted file mode 100644 index d9f6eb35..00000000 --- a/templates/icehouse/glance-registry-paste.ini +++ /dev/null @@ -1,25 +0,0 @@ -# Use this pipeline for no auth - DEFAULT -[pipeline:glance-registry] -pipeline = unauthenticated-context registryapp - -# Use this pipeline for keystone auth -[pipeline:glance-registry-keystone] -pipeline = authtoken context registryapp - -# Use this pipeline for authZ only. This means that the registry will treat a -# user as authenticated without making requests to keystone to reauthenticate -# the user. -[pipeline:glance-registry-trusted-auth] -pipeline = context registryapp - -[app:registryapp] -paste.app_factory = glance.registry.api:API.factory - -[filter:context] -paste.filter_factory = glance.api.middleware.context:ContextMiddleware.factory - -[filter:unauthenticated-context] -paste.filter_factory = glance.api.middleware.context:UnauthenticatedContextMiddleware.factory - -[filter:authtoken] -paste.filter_factory = keystoneclient.middleware.auth_token:filter_factory diff --git a/templates/kilo/glance-api-paste.ini b/templates/kilo/glance-api-paste.ini deleted file mode 100644 index 86a4cdb1..00000000 --- a/templates/kilo/glance-api-paste.ini +++ /dev/null @@ -1,77 +0,0 @@ -# Use this pipeline for no auth or image caching - DEFAULT -[pipeline:glance-api] -pipeline = versionnegotiation osprofiler unauthenticated-context rootapp - -# Use this pipeline for image caching and no auth -[pipeline:glance-api-caching] -pipeline = versionnegotiation osprofiler unauthenticated-context cache rootapp - -# Use this pipeline for caching w/ management interface but no auth -[pipeline:glance-api-cachemanagement] -pipeline = versionnegotiation osprofiler unauthenticated-context cache cachemanage rootapp - -# Use this pipeline for keystone auth -[pipeline:glance-api-keystone] -pipeline = versionnegotiation osprofiler authtoken context rootapp - -# Use this pipeline for keystone auth with image caching -[pipeline:glance-api-keystone+caching] -pipeline = versionnegotiation osprofiler authtoken context cache rootapp - -# Use this pipeline for keystone auth with caching and cache management -[pipeline:glance-api-keystone+cachemanagement] -pipeline = versionnegotiation osprofiler authtoken context cache cachemanage rootapp - -# Use this pipeline for authZ only. This means that the registry will treat a -# user as authenticated without making requests to keystone to reauthenticate -# the user. -[pipeline:glance-api-trusted-auth] -pipeline = versionnegotiation osprofiler context rootapp - -# Use this pipeline for authZ only. This means that the registry will treat a -# user as authenticated without making requests to keystone to reauthenticate -# the user and uses cache management -[pipeline:glance-api-trusted-auth+cachemanagement] -pipeline = versionnegotiation osprofiler context cache cachemanage rootapp - -[composite:rootapp] -paste.composite_factory = glance.api:root_app_factory -/: apiversions -/v1: apiv1app -/v2: apiv2app - -[app:apiversions] -paste.app_factory = glance.api.versions:create_resource - -[app:apiv1app] -paste.app_factory = glance.api.v1.router:API.factory - -[app:apiv2app] -paste.app_factory = glance.api.v2.router:API.factory - -[filter:versionnegotiation] -paste.filter_factory = glance.api.middleware.version_negotiation:VersionNegotiationFilter.factory - -[filter:cache] -paste.filter_factory = glance.api.middleware.cache:CacheFilter.factory - -[filter:cachemanage] -paste.filter_factory = glance.api.middleware.cache_manage:CacheManageFilter.factory - -[filter:context] -paste.filter_factory = glance.api.middleware.context:ContextMiddleware.factory - -[filter:unauthenticated-context] -paste.filter_factory = glance.api.middleware.context:UnauthenticatedContextMiddleware.factory - -[filter:authtoken] -paste.filter_factory = keystonemiddleware.auth_token:filter_factory -delay_auth_decision = true - -[filter:gzip] -paste.filter_factory = glance.api.middleware.gzip:GzipMiddleware.factory - -[filter:osprofiler] -paste.filter_factory = osprofiler.web:WsgiMiddleware.factory -hmac_keys = SECRET_KEY -enabled = yes diff --git a/templates/kilo/glance-registry-paste.ini b/templates/kilo/glance-registry-paste.ini deleted file mode 100644 index df403f6e..00000000 --- a/templates/kilo/glance-registry-paste.ini +++ /dev/null @@ -1,30 +0,0 @@ -# Use this pipeline for no auth - DEFAULT -[pipeline:glance-registry] -pipeline = osprofiler unauthenticated-context registryapp - -# Use this pipeline for keystone auth -[pipeline:glance-registry-keystone] -pipeline = osprofiler authtoken context registryapp - -# Use this pipeline for authZ only. This means that the registry will treat a -# user as authenticated without making requests to keystone to reauthenticate -# the user. -[pipeline:glance-registry-trusted-auth] -pipeline = osprofiler context registryapp - -[app:registryapp] -paste.app_factory = glance.registry.api:API.factory - -[filter:context] -paste.filter_factory = glance.api.middleware.context:ContextMiddleware.factory - -[filter:unauthenticated-context] -paste.filter_factory = glance.api.middleware.context:UnauthenticatedContextMiddleware.factory - -[filter:authtoken] -paste.filter_factory = keystonemiddleware.auth_token:filter_factory - -[filter:osprofiler] -paste.filter_factory = osprofiler.web:WsgiMiddleware.factory -hmac_keys = SECRET_KEY -enabled = yes diff --git a/templates/mitaka/glance-api-paste.ini b/templates/mitaka/glance-api-paste.ini deleted file mode 100644 index d4e3e35e..00000000 --- a/templates/mitaka/glance-api-paste.ini +++ /dev/null @@ -1,108 +0,0 @@ -# Use this pipeline for no auth or image caching - DEFAULT -[pipeline:glance-api] -pipeline = cors healthcheck versionnegotiation osprofiler unauthenticated-context rootapp - -# Use this pipeline for image caching and no auth -[pipeline:glance-api-caching] -pipeline = cors healthcheck versionnegotiation osprofiler unauthenticated-context cache rootapp - -# Use this pipeline for caching w/ management interface but no auth -[pipeline:glance-api-cachemanagement] -pipeline = cors healthcheck versionnegotiation osprofiler unauthenticated-context cache cachemanage rootapp - -# Use this pipeline for keystone auth -[pipeline:glance-api-keystone] -pipeline = cors healthcheck versionnegotiation osprofiler authtoken context rootapp - -# Use this pipeline for keystone auth with image caching -[pipeline:glance-api-keystone+caching] -pipeline = cors healthcheck versionnegotiation osprofiler authtoken context cache rootapp - -# Use this pipeline for keystone auth with caching and cache management -[pipeline:glance-api-keystone+cachemanagement] -pipeline = cors healthcheck versionnegotiation osprofiler authtoken context cache cachemanage rootapp - -# Use this pipeline for authZ only. This means that the registry will treat a -# user as authenticated without making requests to keystone to reauthenticate -# the user. -[pipeline:glance-api-trusted-auth] -pipeline = cors healthcheck versionnegotiation osprofiler context rootapp - -# Use this pipeline for authZ only. This means that the registry will treat a -# user as authenticated without making requests to keystone to reauthenticate -# the user and uses cache management -[pipeline:glance-api-trusted-auth+cachemanagement] -pipeline = cors healthcheck versionnegotiation osprofiler context cache cachemanage rootapp - -[composite:rootapp] -paste.composite_factory = glance.api:root_app_factory -/: apiversions -/v1: apiv1app -/v2: apiv2app -/v3: apiv3app - -[app:apiversions] -paste.app_factory = glance.api.versions:create_resource - -[app:apiv1app] -paste.app_factory = glance.api.v1.router:API.factory - -[app:apiv2app] -paste.app_factory = glance.api.v2.router:API.factory - -[app:apiv3app] -paste.app_factory = glance.api.v3.router:API.factory - -[filter:healthcheck] -paste.filter_factory = oslo_middleware:Healthcheck.factory -backends = disable_by_file -disable_by_file_path = /etc/glance/healthcheck_disable - -[filter:versionnegotiation] -paste.filter_factory = glance.api.middleware.version_negotiation:VersionNegotiationFilter.factory - -[filter:cache] -paste.filter_factory = glance.api.middleware.cache:CacheFilter.factory - -[filter:cachemanage] -paste.filter_factory = glance.api.middleware.cache_manage:CacheManageFilter.factory - -[filter:context] -paste.filter_factory = glance.api.middleware.context:ContextMiddleware.factory - -[filter:unauthenticated-context] -paste.filter_factory = glance.api.middleware.context:UnauthenticatedContextMiddleware.factory - -[filter:authtoken] -paste.filter_factory = keystonemiddleware.auth_token:filter_factory -delay_auth_decision = true - -[filter:gzip] -paste.filter_factory = glance.api.middleware.gzip:GzipMiddleware.factory - -[filter:osprofiler] -paste.filter_factory = osprofiler.web:WsgiMiddleware.factory -hmac_keys = SECRET_KEY #DEPRECATED -enabled = yes #DEPRECATED - -[filter:cors] -paste.filter_factory = oslo_middleware.cors:filter_factory -oslo_config_project = glance -oslo_config_program = glance-api -# Basic Headers (Automatic) -# Accept = Origin, Accept, Accept-Language, Content-Type, Cache-Control, Content-Language, Expires, Last-Modified, Pragma -# Expose = Origin, Accept, Accept-Language, Content-Type, Cache-Control, Content-Language, Expires, Last-Modified, Pragma - -# Glance Headers -# Accept = Content-MD5, X-Image-Meta-Checksum, X-Storage-Token, Accept-Encoding -# Expose = X-Image-Meta-Checksum - -# Keystone Headers -# Accept = X-Auth-Token, X-Identity-Status, X-Roles, X-Service-Catalog, X-User-Id, X-Tenant-Id -# Expose = X-Auth-Token, X-Subject-Token, X-Service-Token - -# Request ID Middleware Headers -# Accept = X-OpenStack-Request-ID -# Expose = X-OpenStack-Request-ID -latent_allow_headers = Content-MD5, X-Image-Meta-Checksum, X-Storage-Token, Accept-Encoding, X-Auth-Token, X-Identity-Status, X-Roles, X-Service-Catalog, X-User-Id, X-Tenant-Id, X-OpenStack-Request-ID -latent_expose_headers = X-Image-Meta-Checksum, X-Auth-Token, X-Subject-Token, X-Service-Token, X-OpenStack-Request-ID \ No newline at end of file diff --git a/templates/mitaka/glance-registry-paste.ini b/templates/mitaka/glance-registry-paste.ini deleted file mode 100644 index 492dbc6f..00000000 --- a/templates/mitaka/glance-registry-paste.ini +++ /dev/null @@ -1,35 +0,0 @@ -# Use this pipeline for no auth - DEFAULT -[pipeline:glance-registry] -pipeline = healthcheck osprofiler unauthenticated-context registryapp - -# Use this pipeline for keystone auth -[pipeline:glance-registry-keystone] -pipeline = healthcheck osprofiler authtoken context registryapp - -# Use this pipeline for authZ only. This means that the registry will treat a -# user as authenticated without making requests to keystone to reauthenticate -# the user. -[pipeline:glance-registry-trusted-auth] -pipeline = healthcheck osprofiler context registryapp - -[app:registryapp] -paste.app_factory = glance.registry.api:API.factory - -[filter:healthcheck] -paste.filter_factory = oslo_middleware:Healthcheck.factory -backends = disable_by_file -disable_by_file_path = /etc/glance/healthcheck_disable - -[filter:context] -paste.filter_factory = glance.api.middleware.context:ContextMiddleware.factory - -[filter:unauthenticated-context] -paste.filter_factory = glance.api.middleware.context:UnauthenticatedContextMiddleware.factory - -[filter:authtoken] -paste.filter_factory = keystonemiddleware.auth_token:filter_factory - -[filter:osprofiler] -paste.filter_factory = osprofiler.web:WsgiMiddleware.factory -hmac_keys = SECRET_KEY #DEPRECATED -enabled = yes #DEPRECATED diff --git a/tests/basic_deployment.py b/tests/basic_deployment.py index 1d90149b..82094ecc 100644 --- a/tests/basic_deployment.py +++ b/tests/basic_deployment.py @@ -476,45 +476,6 @@ class GlanceBasicDeployment(OpenStackAmuletDeployment): message = "glance registry paste config error: {}".format(ret) amulet.raise_status(amulet.FAIL, msg=message) - def _get_filter_factory_expected_dict(self): - """Return expected authtoken filter factory dict for OS release""" - if self._get_openstack_release() >= self.trusty_kilo: - # Kilo and later - val = 'keystonemiddleware.auth_token:filter_factory' - else: - # Juno and earlier - val = 'keystoneclient.middleware.auth_token:filter_factory' - - return {'filter:authtoken': {'paste.filter_factory': val}} - - def test_304_glance_api_paste_auth_config(self): - """Verify authtoken section config in glance-api-paste.ini using - glance/keystone relation data.""" - u.log.debug('Checking glance api paste config file...') - unit = self.glance_sentry - conf = '/etc/glance/glance-api-paste.ini' - expected = self._get_filter_factory_expected_dict() - - for section, pairs in expected.iteritems(): - ret = u.validate_config_data(unit, conf, section, pairs) - if ret: - message = "glance api paste config error: {}".format(ret) - amulet.raise_status(amulet.FAIL, msg=message) - - def test_306_glance_registry_paste_auth_config(self): - """Verify authtoken section config in glance-registry-paste.ini using - glance/keystone relation data.""" - u.log.debug('Checking glance registry paste config file...') - unit = self.glance_sentry - conf = '/etc/glance/glance-registry-paste.ini' - expected = self._get_filter_factory_expected_dict() - - for section, pairs in expected.iteritems(): - ret = u.validate_config_data(unit, conf, section, pairs) - if ret: - message = "glance registry paste config error: {}".format(ret) - amulet.raise_status(amulet.FAIL, msg=message) - def test_410_glance_image_create_delete(self): """Create new cirros image in glance, verify, then delete it.""" u.log.debug('Creating, checking and deleting glance image...') diff --git a/unit_tests/test_glance_relations.py b/unit_tests/test_glance_relations.py index b88792ba..f2385a42 100644 --- a/unit_tests/test_glance_relations.py +++ b/unit_tests/test_glance_relations.py @@ -55,6 +55,7 @@ TO_PATCH = [ 'ceph_config_file', 'git_install', 'update_nrpe_config', + 'reinstall_paste_ini', # other 'call', 'check_call', @@ -496,9 +497,7 @@ class GlanceRelationTests(CharmTestCase): self.relation_ids.return_value = [] relations.keystone_changed() self.assertEquals([call('/etc/glance/glance-api.conf'), - call('/etc/glance/glance-registry.conf'), - call('/etc/glance/glance-api-paste.ini'), - call('/etc/glance/glance-registry-paste.ini')], + call('/etc/glance/glance-registry.conf')], configs.write.call_args_list) self.assertTrue(configure_https.called) @@ -517,9 +516,7 @@ class GlanceRelationTests(CharmTestCase): self.relation_ids.return_value = ['object-store:0'] relations.keystone_changed() self.assertEquals([call('/etc/glance/glance-api.conf'), - call('/etc/glance/glance-registry.conf'), - call('/etc/glance/glance-api-paste.ini'), - call('/etc/glance/glance-registry-paste.ini')], + call('/etc/glance/glance-registry.conf')], configs.write.call_args_list) object_store_joined.assert_called_with() self.assertTrue(configure_https.called) @@ -620,6 +617,7 @@ class GlanceRelationTests(CharmTestCase): relations.upgrade_charm() self.apt_install.assert_called_with(['test'], fatal=True) self.assertTrue(configs.write_all.called) + self.assertTrue(self.reinstall_paste_ini.called) def test_ha_relation_joined(self): self.get_hacluster_config.return_value = { diff --git a/unit_tests/test_glance_utils.py b/unit_tests/test_glance_utils.py index c796eed2..31a9575f 100644 --- a/unit_tests/test_glance_utils.py +++ b/unit_tests/test_glance_utils.py @@ -8,6 +8,7 @@ os.environ['JUJU_UNIT_NAME'] = 'glance' import hooks.glance_utils as utils from test_utils import ( CharmTestCase, + SimpleKV, ) TO_PATCH = [ @@ -66,8 +67,6 @@ class TestGlanceUtils(CharmTestCase): calls = [] for conf in [utils.GLANCE_REGISTRY_CONF, utils.GLANCE_API_CONF, - utils.GLANCE_API_PASTE_INI, - utils.GLANCE_REGISTRY_PASTE_INI, utils.HAPROXY_CONF, utils.HTTPS_APACHE_CONF]: calls.append( @@ -85,8 +84,6 @@ class TestGlanceUtils(CharmTestCase): calls = [] for conf in [utils.GLANCE_REGISTRY_CONF, utils.GLANCE_API_CONF, - utils.GLANCE_API_PASTE_INI, - utils.GLANCE_REGISTRY_PASTE_INI, utils.HAPROXY_CONF, utils.HTTPS_APACHE_24_CONF]: calls.append( @@ -105,8 +102,6 @@ class TestGlanceUtils(CharmTestCase): calls = [] for conf in [utils.GLANCE_REGISTRY_CONF, utils.GLANCE_API_CONF, - utils.GLANCE_API_PASTE_INI, - utils.GLANCE_REGISTRY_PASTE_INI, utils.HAPROXY_CONF, utils.ceph_config_file()]: calls.append( @@ -122,8 +117,6 @@ class TestGlanceUtils(CharmTestCase): ex_map = OrderedDict([ (utils.GLANCE_REGISTRY_CONF, ['glance-registry']), (utils.GLANCE_API_CONF, ['glance-api']), - (utils.GLANCE_API_PASTE_INI, ['glance-api']), - (utils.GLANCE_REGISTRY_PASTE_INI, ['glance-registry']), (utils.ceph_config_file(), ['glance-api', 'glance-registry']), (utils.HAPROXY_CONF, ['haproxy']), (utils.HTTPS_APACHE_CONF, ['apache2']), @@ -327,3 +320,36 @@ class TestGlanceUtils(CharmTestCase): "TEST CONFIG", utils.REQUIRED_INTERFACES, charm_func=utils.check_optional_relations) + + @patch.object(utils, 'os') + @patch.object(utils, 'kv') + def test_reinstall_paste_ini(self, kv, _os): + """Ensure that paste.ini files are re-installed""" + _os.path.exists.return_value = True + test_kv = SimpleKV() + kv.return_value = test_kv + utils.reinstall_paste_ini() + self.apt_install.assert_called_with( + packages=['glance-api', 'glance-registry'], + options=utils.REINSTALL_OPTIONS, + fatal=True + ) + _os.path.exists.assert_has_calls([ + call(utils.GLANCE_REGISTRY_PASTE), + call(utils.GLANCE_API_PASTE), + ]) + _os.remove.assert_has_calls([ + call(utils.GLANCE_REGISTRY_PASTE), + call(utils.GLANCE_API_PASTE), + ]) + self.assertTrue(test_kv.get(utils.PASTE_INI_MARKER)) + self.assertTrue(test_kv.flushed) + + @patch.object(utils, 'kv') + def test_reinstall_paste_ini_idempotent(self, kv): + """Ensure that re-running does not re-install files""" + test_kv = SimpleKV() + test_kv.set(utils.PASTE_INI_MARKER, True) + kv.return_value = test_kv + utils.reinstall_paste_ini() + self.assertFalse(self.apt_install.called) diff --git a/unit_tests/test_utils.py b/unit_tests/test_utils.py index 64bd0d92..3e383efc 100644 --- a/unit_tests/test_utils.py +++ b/unit_tests/test_utils.py @@ -105,6 +105,22 @@ class TestRelation(object): return None +class SimpleKV(object): + '''Simple in-memory replacement for unitdata.kv''' + def __init__(self): + self.flushed = False + self.data = {} + + def get(self, key, default=None): + return self.data.get(key, default) + + def set(self, key, value): + self.data[key] = value + + def flush(self): + self.flushed = True + + @contextmanager def patch_open(): '''Patch open() to allow mocking both open() itself and the file that is