Issue #3541
Updated by bmbouter about 5 years ago
h3. General Problem: Some plugins have validation requirements for the membership of content units in a repository. The validation required depends on the plugin and the content type. Currently add/remove is done with a POST to v3/repositories/1234/versions/. This does not involve the plugins at all, so there is no opportunity for plugins to create these validations. h3. Example Problem with Docker: ManifestLists cannot be added unless the Manifests they refer to are also in the repository. L1 is ManifestList, and it refers to (contains) 2 Manifests, M1, M2. The repository is considered corrupt if it contains L1, but not M1 and M2. M1 and M2 could be in the repository without L1. h2. Solutions: h3. Add a plugin opportunity to be involved Validate The RepositoryVersion.create() method will take Allow a new, optional parameter called <code>handler</code>. A new object will be created in pulpcore.plugin.handlers.RepositoryVersionHandler. plugin to define a <code>validate_repo_version</code> <pre><code class="python"> <pre> class RepositoryVersionHandler: DockerManifestList(Content): def add_content(self, qs_add_content, repo_version): pass ... @staticmethod def remove_content(self, qs_remove_content, validate_repo_version(qs_content, repo_version): pass def validate(self, repo_version): """ Validates a repository version's DockerManifestList units. This method specifically ensures that the repo has all the right DockerManifests that correspond # with this method should only DockerManifestList Args: qs_content (django.Queryset): A Queryset of the detail instances for this content type repo_version (RepositoryVersion): The Repository version to validate Raises: django.core.exceptions.ValidationError: if the repository is invalid pass def repo_key_implementation(qs_add_content, repo_version, model_class, repo_unit_key): """ # the implementation check all of repo_key # not enabled by default # remove the validation related DockerManifestList content form repo_version when repo_unit_key if has_a_validation_problem: raise ValidationException('Repo foo has "another one" already in it. ManifestList Y but is missing Manifest Z') </code></pre> Then </pre> h3. Add a subclass would be: plugin add_content and remove_content <pre><code class="python"> <pre> class FileRepositoryVersionHandler(RepositoryVersionHandler): ModuleMD(Content): ... @staticmethod def __init__(custom_foo, custom_bar): add_content(qa_add_content, repo_version): """ Handles any extra work needed for ModuleMD content being added to a RepositoryVersion. Args: qs_content (django.Queryset): A Queryset of the detail instances for this content type repo_version (RepositoryVersion): The Repository version the content is being added to """ # allow for customization here from params. pass check on if I need to also add some other content also @staticmethod def add_content(self, qs_add_content, remove_content(qa_add_content, repo_version): cls.repo_key_implementation(qs_add_content, repo_version, FileContent, 'relative_path') # This effectively "enables" the repo_key functionality here instead """ Handles any extra work needed for ModuleMD content being removed from a RepositoryVersion. Args: qs_content (django.Queryset): A Queryset of on the Content object. </code></pre> detail instances for this content type repo_version (RepositoryVersion): The plugin will create an instance of their subclass, e.g. <code>FileRepositoryVersionHandler</code>, configured how they want, and then pass it to RepositoryVersion.create() like: <pre><code class="python"> my_handler Repository version the content is being removed from """ # check if I should also remove corresponding RPMs # qa_rpms_to_remove = FileRepositoryVersionHandler('a', 'b') ... if qa_rpms_to _remove repo_version.remove_content(qa_rpms_to_remove, call_plugin_hook=False) RepositoryVersion.create(handler=my_handler) </code></pre> </pre> Providing It's possible we'll have a RepositoryVersionHandler is optional. recursive call h3. Integration with DeclarativeVersion RepositoryVersion.remove_content -> ModuleMD.remove_content -> RepositoryVersion.remove_content so the 'if' statement will prevent it by not causing an additional call to ModuleMD.remove_content if there are 0 items to remove. A new, optional The "existing add_content and remove_content": calls will receive an additional parameter called <code>handler</code> will be added <code>call_plugin_hook</code> which defaults to <code>DeclarativeVersion</code> which True. If True, the plugin callback will also accept and use the handler for various operations on the RepositoryVersion. occur, otherwise it won't.