From 0a1d645000576e5ccbc723614e0610eeffd29935 Mon Sep 17 00:00:00 2001 From: Rafael Lopez Date: Wed, 1 Mar 2023 01:32:01 +0000 Subject: [PATCH] Add configuration option for custom image properties Added configuration for injecting default image properties using the interoperable image import process. This change adds 1 configuration option to the charm: - 'custom-import-properties' to specify the desired custom properties Note: current glance docs example shows incorrect quoting, the implemented format in this change (no quotes) is correct. Docs fix in: https://review.opendev.org/c/openstack/glance/+/890423 Release note: https://review.opendev.org/c/openstack/charm-guide/+/891010 Closes-Bug: 1994053 Related-Bug: 2028895 Change-Id: I9548c90e663285c6e7a70eebc8c135a5568974bc --- config.yaml | 10 ++++++++ hooks/glance_contexts.py | 36 +++++++++++++++++++++++++++- templates/parts/section-image-import | 12 +++++++--- unit_tests/test_glance_contexts.py | 28 ++++++++++++++++++++++ 4 files changed, 82 insertions(+), 4 deletions(-) diff --git a/config.yaml b/config.yaml index 16a83da5..6d65b883 100644 --- a/config.yaml +++ b/config.yaml @@ -556,6 +556,16 @@ options: only supported on stein or newer. This only works on imported images (for example using 'openstack image create --import') Does not work on regular image uploads (like 'openstack image create') + custom-import-properties: + type: string + default: + description: | + Set custom image properties when images are imported using the + interoperable image import process. Properties and their value are + delimited by commas, and values with colons and spaces are possible. + The properties specified here will be added to the glance-api + configuration file without being validated. + Example: 'prop1:val1,prop2:val-2,prop3:val:with:colons' s3-store-host: type: string default: diff --git a/hooks/glance_contexts.py b/hooks/glance_contexts.py index 72c9f47f..d91c1681 100644 --- a/hooks/glance_contexts.py +++ b/hooks/glance_contexts.py @@ -107,10 +107,44 @@ class GlanceImageImportContext(OSContextGenerator): def __call__(self): ctxt = {} + ctxt['image_import_plugins'] = [] if config('image-conversion'): - ctxt['image_conversion'] = config('image-conversion') + ctxt['image_import_plugins'].append('image_conversion') + + if config('custom-import-properties'): + try: + self.validate_custom_import_properties() + ctxt['image_import_plugins'].append('inject_image_metadata') + ctxt['custom_import_properties'] = ( + config('custom-import-properties') + ) + except (ValueError): + juju_log('Unable to validate custom-import-properties ({}), ' + 'see config.yaml for information about valid ' + 'formatting' + .format(config('custom-import-properties')), + level=ERROR) + raise return ctxt + def validate_custom_import_properties(self): + """Check the format of 'custom-import-properties' config parameter, + it should be a string of comma delimited key:value pairs + """ + props = config('custom-import-properties') + if not isinstance(props, str): + raise ValueError('not a string') + # Empty string is valid + if props == '': + return + # Check key value pairs + props_list = props.split(',') + for prop in props_list: + if ":" not in prop: + raise ValueError('value not found for property: {}' + .format(prop)) + return + class CephGlanceContext(OSContextGenerator): interfaces = ['ceph-glance'] diff --git a/templates/parts/section-image-import b/templates/parts/section-image-import index afd1cbd4..a9e3075e 100644 --- a/templates/parts/section-image-import +++ b/templates/parts/section-image-import @@ -1,7 +1,13 @@ -{% if image_conversion -%} +{% if image_import_plugins|length > 0 -%} [image_import_opts] -image_import_plugins = ['image_conversion'] - +image_import_plugins = {{ image_import_plugins }} +{% if 'image_conversion' in image_import_plugins %} [image_conversion] output_format = raw +{% endif %} +{% if 'inject_image_metadata' in image_import_plugins -%} +[inject_metadata_properties] +ignore_user_roles = "" +inject = {{ custom_import_properties }} {% endif -%} +{% endif %} diff --git a/unit_tests/test_glance_contexts.py b/unit_tests/test_glance_contexts.py index 2f83c14b..e4749d43 100644 --- a/unit_tests/test_glance_contexts.py +++ b/unit_tests/test_glance_contexts.py @@ -75,6 +75,34 @@ class TestGlanceContexts(CharmTestCase): "/var/lib/glance/images/", 'image_size_cap': 1099511627776}) + def test_glance_image_import_context(self): + config = { + 'image-conversion': True, + 'custom-import-properties': 'p1:v1,prop2:rng1:rng2'} + self.config.side_effect = lambda x: config[x] + self.assertEqual(contexts.GlanceImageImportContext()(), + {'image_import_plugins': [ + 'image_conversion', + 'inject_image_metadata'], + 'custom_import_properties': + 'p1:v1,prop2:rng1:rng2'}) + config = { + 'image-conversion': False, + 'custom-import-properties': ''} + self.config.side_effect = lambda x: config[x] + self.assertEqual(contexts.GlanceImageImportContext()(), + {'image_import_plugins': []}) + + config = { + 'image-conversion': False, + 'custom-import-properties': 'prop-noval'} + self.assertRaises(ValueError) + + config = { + 'image-conversion': False, + 'custom-import-properties': 10} + self.assertRaises(ValueError) + def test_swift_not_related(self): self.relation_ids.return_value = [] self.assertEqual(contexts.ObjectStoreContext()(), {})