diff --git a/glare/engine.py b/glare/engine.py index 0e834e7..9a67f41 100644 --- a/glare/engine.py +++ b/glare/engine.py @@ -340,7 +340,13 @@ class Engine(object): policy.authorize(action_name, af.to_dict(), context) af.validate_delete(context, af) blobs = af.delete(context, af) - if not CONF.delayed_delete: + + delayed_delete = getattr(CONF, type_name).delayed_delete + # use global parameter if delayed delete isn't set per artifact type + if delayed_delete is None: + delayed_delete = CONF.delayed_delete + + if not delayed_delete: if blobs: # delete blobs one by one self._delete_blobs(context, af, blobs) diff --git a/glare/objects/base.py b/glare/objects/base.py index f40b379..09b6288 100644 --- a/glare/objects/base.py +++ b/glare/objects/base.py @@ -26,15 +26,16 @@ from glare.objects.meta import fields as glare_fields from glare.objects.meta import validators from glare.objects.meta import wrappers -artifact_opts = [ +global_artifact_opts = [ cfg.BoolOpt('delayed_delete', default=False, - help=_("Defines if artifact must be deleted immediately " - "or just marked as deleted so it can be cleaned " + help=_("If False defines that artifacts must be deleted " + "immediately after the user call. Otherwise they just " + "will be marked as deleted so they can be scrubbed " "by some other tool in the background.")), ] CONF = cfg.CONF -CONF.register_opts(artifact_opts) +CONF.register_opts(global_artifact_opts) LOG = logging.getLogger(__name__) @@ -123,6 +124,25 @@ class BaseArtifact(base.VersionedObject): description="Artifact version(semver).") } + artifact_type_opts = [ + cfg.BoolOpt('delayed_delete', + help=_( + "If False defines that artifacts must be deleted " + "immediately after the user call. Otherwise they just " + "will be marked as deleted so they can be scrubbed " + "by some other tool in the background. " + "Redefines global parameter of the same name " + "from [DEFAULT] section.")), + ] + + def __new__(cls, *args, **kwargs): + CONF.register_opts(cls.artifact_type_opts, group=cls.get_type_name()) + return base.VersionedObject.__new__(cls) + + @classmethod + def list_artifact_type_opts(cls): + return cls.artifact_type_opts + db_api = artifact_api.ArtifactAPI() @classmethod diff --git a/glare/opts.py b/glare/opts.py index 83bfd8b..f861708 100644 --- a/glare/opts.py +++ b/glare/opts.py @@ -29,7 +29,7 @@ import glare.common.config import glare.common.wsgi import glare.notification import glare.objects.base -import glare.objects.meta.registry +from glare.objects.meta import registry import glare.scrubber _artifacts_opts = [ @@ -42,8 +42,8 @@ _artifacts_opts = [ glare.common.wsgi.eventlet_opts, glare.common.wsgi.socket_opts, glare.notification.notifier_opts, - glare.objects.base.artifact_opts, - glare.objects.meta.registry.registry_options))), + glare.objects.base.global_artifact_opts, + registry.registry_options))), profiler.list_opts()[0], ('paste_deploy', glare.common.config.paste_deploy_opts), ('keycloak_oidc', glare.api.middleware.keycloak_auth.keycloak_oidc_opts), @@ -53,6 +53,11 @@ _artifacts_opts = [ glare.scrubber.scrubber_cmd_cli_opts) ] +registry.ArtifactRegistry.register_all_artifacts() +for af_type in registry.ArtifactRegistry.obj_classes().values(): + _artifacts_opts.append( + (af_type[0].get_type_name(), af_type[0].list_artifact_type_opts())) + def list_artifacts_opts(): """Return a list of oslo_config options available in Glare""" diff --git a/glare/tests/unit/api/test_delete.py b/glare/tests/unit/api/test_delete.py index d23ad56..978b23f 100644 --- a/glare/tests/unit/api/test_delete.py +++ b/glare/tests/unit/api/test_delete.py @@ -151,7 +151,7 @@ class TestArtifactUpdate(base.BaseTestArtifactAPI): @mock.patch('glare.common.store_api.delete_blob', side_effect=store_api.delete_blob) - def test_delayed_delete(self, mocked_delete): + def test_delayed_delete_global(self, mocked_delete): # Enable delayed delete self.config(delayed_delete=True) # Delete artifact and check that 'delete_blob' was not called @@ -172,3 +172,28 @@ class TestArtifactUpdate(base.BaseTestArtifactAPI): self.assertEqual(1, mocked_delete.call_count) self.assertRaises(exc.NotFound, self.controller.show, self.req, 'sample_artifact', self.artifact['id']) + + @mock.patch('glare.common.store_api.delete_blob', + side_effect=store_api.delete_blob) + def test_delayed_delete_per_artifact_type(self, mocked_delete): + # Enable delayed delete for sample_artifact type + # Global parameter is disabled + self.config(delayed_delete=True, group='sample_artifact') + # Delete artifact and check that 'delete_blob' was not called + self.controller.delete(self.req, 'sample_artifact', + self.artifact['id']) + self.assertEqual(0, mocked_delete.call_count) + # Check that artifact status is 'deleted' and its blob is + # 'pending_delete' + self.artifact = self.controller.show( + self.req, 'sample_artifact', self.artifact['id']) + self.assertEqual('deleted', self.artifact['status']) + self.assertEqual('active', self.artifact['blob']['status']) + # Disable delayed delete + self.config(delayed_delete=False, group='sample_artifact') + # Delete artifact and check that 'delete_blob' was called this time + self.controller.delete(self.req, 'sample_artifact', + self.artifact['id']) + self.assertEqual(1, mocked_delete.call_count) + self.assertRaises(exc.NotFound, self.controller.show, + self.req, 'sample_artifact', self.artifact['id'])