3
3
4
4
from django .conf import settings
5
5
from jsonschema import Draft7Validator
6
- from pulpcore .plugin .models import AsciiArmoredDetachedSigningService , Publication , Remote
6
+ from pulpcore .plugin .models import AsciiArmoredDetachedSigningService , Publication , Remote , Content
7
7
from pulpcore .plugin .serializers import (
8
8
DetailRelatedField ,
9
9
DistributionSerializer ,
14
14
RepositorySyncURLSerializer ,
15
15
ValidateFieldsMixin ,
16
16
)
17
- from pulpcore .plugin .util import get_domain
17
+ from pulpcore .plugin .util import get_domain , extract_pk
18
18
from rest_framework import serializers
19
19
from pulp_rpm .app .fields import CustomJSONField
20
20
37
37
)
38
38
from pulp_rpm .app .schema import COPY_CONFIG_SCHEMA
39
39
from urllib .parse import urlparse
40
+ from textwrap import dedent
40
41
41
42
42
43
class RpmRepositorySerializer (RepositorySerializer ):
@@ -543,7 +544,32 @@ class CopySerializer(ValidateFieldsMixin, serializers.Serializer):
543
544
"""
544
545
545
546
config = CustomJSONField (
546
- help_text = _ ("A JSON document describing sources, destinations, and content to be copied" ),
547
+ help_text = _ (
548
+ dedent (
549
+ """\
550
+ A JSON document describing sources, destinations, and content to be copied.
551
+
552
+ Its a list of dictionaries with the following available fields:
553
+
554
+ ```json
555
+ [
556
+ {
557
+ "source_repo_version": <RepositoryVersion [pulp_href|prn]>,
558
+ "dest_repo": <RpmRepository [pulp_href|prn]>,
559
+ "dest_base_version": <int>,
560
+ "content": [<Content [pulp_href|prn]>, ...]
561
+ },
562
+ ...
563
+ ]
564
+ ```
565
+
566
+ If domains are enabled, the refered pulp objects must be part of the current domain.
567
+
568
+ For usage examples, refer to the advanced copy guide:
569
+ <https://pulpproject.org/pulp_rpm/docs/user/guides/modify/#advanced-copy-workflow>
570
+ """
571
+ )
572
+ ),
547
573
)
548
574
549
575
dependency_solving = serializers .BooleanField (
@@ -558,14 +584,24 @@ def validate(self, data):
558
584
Check for cross-domain references (if domain-enabled).
559
585
"""
560
586
561
- def check_domain (domain , href , name ):
562
- # We're doing just a string-check here rather than checking objects
563
- # because there can be A LOT of objects, and this is happening in the view-layer
564
- # where we have strictly-limited timescales to work with
587
+ def _extract_pk (ref ):
588
+ """Workaround for expensive extract_pk(href) calls."""
589
+ if ref .starswith ("prn:" ):
590
+ return extract_pk (ref )
591
+ else :
592
+ # This assumes ref is a non-nested href. E.g:
593
+ # /pulp/api/v3/content/rpm/packagegroups/0194a901-a9a6-705a-9436-ba6e19c98f40/
594
+ uuid = urlparse (ref ).path .strip ("/" ).split ("/" )[- 1 ]
595
+ if len (uuid ) < 32 :
596
+ raise serializers .ValidationError (
597
+ _ ("The href path should end with a uuid pk: {}" .format (ref ))
598
+ )
599
+ return uuid
600
+
601
+ def check_domain_ok (domain , href ) -> bool :
565
602
if href and domain not in href :
566
- raise serializers .ValidationError (
567
- _ ("{} must be a part of the {} domain." ).format (name , domain )
568
- )
603
+ return False
604
+ return True
569
605
570
606
def check_cross_domain_config (cfg ):
571
607
"""Check that all config-elts are in 'our' domain."""
@@ -574,13 +610,25 @@ def check_cross_domain_config(cfg):
574
610
# Insure curr-domain exists in src/dest/dest_base_version/content-list hrefs
575
611
curr_domain_name = get_domain ().name
576
612
for entry in cfg :
577
- check_domain (curr_domain_name , entry ["source_repo_version" ], "dest_repo" )
578
- check_domain (curr_domain_name , entry ["dest_repo" ], "dest_repo" )
579
- check_domain (
580
- curr_domain_name , entry .get ("dest_base_version" , None ), "dest_base_version"
581
- )
582
- for content_href in entry .get ("content" , []):
583
- check_domain (curr_domain_name , content_href , "content" )
613
+ domain_ok = True
614
+ ref_list = [entry ["dest_version" ]]
615
+
616
+ # Special case for source_repo_version, because the general method using extract_pk
617
+ # can't be used for nested hrefs (the pk is not in the href).
618
+ if (source_rv := entry ["source_repo_version" ]).startswith ("prn:" ):
619
+ ref_list .append (source_rv )
620
+ else :
621
+ domain_ok = domain_ok and check_domain_ok (curr_domain_name , source_rv )
622
+
623
+ # Check if all references belong to the correct domain without individual calls
624
+ ref_list .extend (entry .get ("content" , []))
625
+ pks_list = (_extract_pk (v ) for v in ref_list )
626
+ distinct = Content .objects .filter (pk__in = pks_list ).values ("pulp_domain" ).distinct ()
627
+ domain_ok = domain_ok and (len (distinct ) == 1 and distinct .name == curr_domain_name )
628
+ if not domain_ok :
629
+ raise serializers .ValidationError (
630
+ _ ("All href/prns must be a part of the {} domain." ).format (curr_domain_name )
631
+ )
584
632
585
633
super ().validate (data )
586
634
if "config" in data :
0 commit comments