Story #3285
Updated by amacdona@redhat.com almost 7 years ago
Tasks that create new RepositoryVersions have to write to pulpcore tables in transactions, set up a working directory, finalize the new version and clean up the database if the task fails. All of this should be handled by a context manager that is a part of the plugin API. h2. Proposal: Wrapper for the RepositoryVersion object in the plugin API The object can be used as a This context manager that creates RepositoryVersions. The pulpcore model should: # Prepare for RepositoryVersion will be removed from the plugin API, so the wrapper will be the only supported way for plugin writers to create new RepositoryVersions, and must be performed only in celery tasks. It will contain all business logic for creation, as well as adding and removing content. The wrapper will be passed to the changeset. h3. example usage task ## Create a new RepositoryVersion. Plugin writers can use this object to add/remove content. Or they can pass it to the ChangeSet. <pre><code class="python"> with RepositoryVersion.create(repository) as created_version: created_version.add_content(some_content) changeset = ChangeSet(created_version, ...) </code></pre> The wrapper object can also be used to access the RepositoryVersion model layer, which replaces the use of plugin.models.RepositoryVersion.objects.get() with: <pre><code class="python"> latest_version = RepositoryVersion.latest(repository) RepositoryVersion(complete=False number=respository.last_version+1) for content in latest_version.content(): </code></pre> The wrapper will provide any functions that plugin writers need to act safely upon the RepositoryVersion and related tables. It will enforce correct usage (and therefore RepositoryVersion immutability) by: ## Increment RepositoryVersion.repository.last_version # ensuring it is only run in tasks ## Create a CreatedResource(content_object=new_version) ## Create a working directory # exclusive control of the RepositoryVersion.complete flag Finalize after a successful task # only allowing repository versions to add and remove content if ## RepositoryVersion.complete = True ## unset working directory # using transactions to create Clean up after a failed task ## Delete new version and update related tables # handle failure scenarios by cleaning up incomplete work ## delete created resource ## do not decrement new_version.repository.last_version The business logic that will live in this class This list is psuedo-implemented in this comment: https://pulp.plan.io/issues/3285#note-6 Working code that will be moved to this class currently lives in: # based on the work done by the File plugin's sync task: https://github.com/asmacdo/pulp_file/blob/a8997383e2a64f915769db629435ae4abecf576c/pulp_file/app/tasks.py#L52-L79 # RepositoryVersion django model in models.repository.RepositoryVersion h3. Review of plugin API changes The RepositoryVersion django model (models.repository.RepositoryVersion) will be removed from the plugin API. Plugin writers should use the RepositoryVersion wrapper/facade instead of the model directly. New/incomplete repository versions will be used exclusively within the context_manager. The ChangeSet will no longer accept a models.repository.RepositoryVersion object, and will instead accept an instance of the class created by this story. The changes above allow us to make the following promise to users: "Once a RepositoryVersion is marked complete, it cannot be altered by any plugin." They also limit the access to the pulpcore data model, which reduces the likelihood of plugin writer errors. We can promise the plugin writer: # When dealing with new RepositoryVersions, your code will either run successfully or the pulpcore data layer will be cleaned up. # All pulpcore actions that must take place together in transactions is handled for you under the hood. # The object you have access to is guarded against unsafe use.