Summary
ArtifactFileField.pre_save uses a fragile prefix check to decide whether an incoming file is already inside artifact storage:
https://github.com/pulp/pulpcore/blob/main/pulpcore/app/models/fields.py#L69-L73
is_in_artifact_storage = file.name.startswith(os.path.join(settings.MEDIA_ROOT, "artifact"))
When settings.MEDIA_ROOT is "" — which is the case for object-storage backends (azure, s3, etc.) — this collapses to:
file.name.startswith("artifact")
file.name for a fresh upload is the client-provided filename. Any uploaded file whose name begins with the substring artifact (e.g. artifact-foo-1.0-1.noarch.rpm) therefore matches, already_in_place is False, and pre_save raises:
ValueError: The file referenced by the Artifact is already present in Artifact storage.
Files must be stored outside this location prior to Artifact creation.
which surfaces to the client as a 500 Internal Server Error on POST /pulp/<domain>/api/v3/artifacts/.
Impact
Any user uploading an artifact whose filename starts with artifact to a deployment with an empty MEDIA_ROOT (object storage) gets a spurious 500. The bug is filename-dependent and backend-dependent, so it is easy to miss.
How it was discovered
A pulp_rpm functional test (test_domains.py::test_artifact_from_file) built an RPM named artifact-<hex>-1.0-1.noarch.rpm and uploaded it via the artifacts endpoint. It failed only on the azure job (empty MEDIA_ROOT); the sibling test using a content- prefix passed.
Suggested fix
The prefix check is meant to detect files that are already physically in artifact storage. It should not rely on a bare startswith against a possibly-empty settings.MEDIA_ROOT, and it arguably should consider the current domain's actual storage location rather than the global setting. Options:
- Normalize/join paths and compare path components instead of doing a raw string prefix match.
- Guard against an empty
MEDIA_ROOT so the check does not degenerate to startswith("artifact").
Environment
- Reproducible with any object-storage backend where
MEDIA_ROOT is empty (azure/s3), including per-domain FileSystem storage where the global settings.MEDIA_ROOT is still empty.
Summary
ArtifactFileField.pre_saveuses a fragile prefix check to decide whether an incoming file is already inside artifact storage:https://github.com/pulp/pulpcore/blob/main/pulpcore/app/models/fields.py#L69-L73
When
settings.MEDIA_ROOTis""— which is the case for object-storage backends (azure, s3, etc.) — this collapses to:file.namefor a fresh upload is the client-provided filename. Any uploaded file whose name begins with the substringartifact(e.g.artifact-foo-1.0-1.noarch.rpm) therefore matches,already_in_placeisFalse, andpre_saveraises:which surfaces to the client as a 500 Internal Server Error on
POST /pulp/<domain>/api/v3/artifacts/.Impact
Any user uploading an artifact whose filename starts with
artifactto a deployment with an emptyMEDIA_ROOT(object storage) gets a spurious 500. The bug is filename-dependent and backend-dependent, so it is easy to miss.How it was discovered
A pulp_rpm functional test (
test_domains.py::test_artifact_from_file) built an RPM namedartifact-<hex>-1.0-1.noarch.rpmand uploaded it via the artifacts endpoint. It failed only on the azure job (emptyMEDIA_ROOT); the sibling test using acontent-prefix passed.Suggested fix
The prefix check is meant to detect files that are already physically in artifact storage. It should not rely on a bare
startswithagainst a possibly-emptysettings.MEDIA_ROOT, and it arguably should consider the current domain's actual storage location rather than the global setting. Options:MEDIA_ROOTso the check does not degenerate tostartswith("artifact").Environment
MEDIA_ROOTis empty (azure/s3), including per-domainFileSystemstorage where the globalsettings.MEDIA_ROOTis still empty.