Problem: Deleting Remote deletes RemoteArtifacts

https://pulp.plan.io/issues/8305

"on_demand" synced content (ContentArtifacts) reference RemoteArtifact rather than Artifact. Deleting the Remote used by the sync deletes all RemoteArtifacts referencing that Remote. Nothing prevents the deletion of the Remote even if doing so would invalidate the ContentArtifacts.

These ContentArtifacts are no longer downloadable, and no longer publishable, regardless of which repository they exist in, unless they have a second RemoteArtifact. If Content were copied between many repositories, then it is possible that deleting the remote could invalidate many repositories at once, or invalidate repositories far enough removed from the user's mind that they don't immediately encounter the issue.

Solution 1: Prevent deletion of the remote

(Still carries the problem that remote can be edited and break ContentArtifacts)

Method 1a

Add a constraint that enforces "artifact is not Null OR remote_artifact is not Null". Deleting the Remote would trigger the ConstraintViolation and fail.

Method 1b:

Prevent deletion of the remote outright, but with an option to proceed by immediately downloading all content on remote deletion

Method 1c:

Check that each content_artifact has at least one other remote artifact, then delete.

Method 1d:

Relax the uniqueness constraint on remote names, and just mark "deleted" remotes as hidden and inaccessible from the API.

diff --git a/pulpcore/app/models/repository.py b/pulpcore/app/models/repository.py index a42c3c136..d5b2b6e0b 100644 --- a/pulpcore/app/models/repository.py +++ b/pulpcore/app/models/repository.py @@ -242,7 +242,8 @@ class Remote(MasterModel): ), ) - name = models.TextField(db_index=True, unique=True) + hidden = models.BooleanField(default=False) + name = models.TextField(db_index=True) url = models.TextField() @@ -391,6 +392,9 @@ class Remote(MasterModel): class Meta: default_related_name = "remotes" + constraints = [ + models.UniqueConstraint(fields=['name'], condition=models.Q(hidden=False)) + ]

https://docs.djangoproject.com/en/3.1/ref/models/constraints/#condition

Method 1e:

Add a force flag to the DELETE call. If force is not specified, and the remote is referenced by either a repository or a remote artifact, the call will fail.

Select a repo