@@ -42,12 +42,18 @@ def __init__(self, **kwargs):
4242# implementations of these interfaces by calling init_multi()
4343realm = None
4444user = None
45+ non_account_user = None
46+ non_account_alt_user = None
4547config = None
46- def init_multi (_realm , _user , _config = None ):
48+ def init_multi (_realm , _user , _non_account_user , _non_account_alt_user , _config = None ):
4749 global realm
4850 realm = _realm
4951 global user
5052 user = _user
53+ global non_account_user
54+ non_account_user = _non_account_user
55+ global non_account_alt_user
56+ non_account_alt_user = _non_account_alt_user
5157 global config
5258 config = _config or Config ()
5359 realm_meta_checkpoint (realm )
@@ -468,17 +474,31 @@ class ZonegroupConns:
468474 def __init__ (self , zonegroup ):
469475 self .zonegroup = zonegroup
470476 self .zones = []
477+ self .non_account_zones = []
478+ self .non_account_alt_zones = []
471479 self .ro_zones = []
480+ self .non_account_ro_zones = []
481+ self .non_account_alt_ro_zones = []
472482 self .rw_zones = []
483+ self .non_account_rw_zones = []
484+ self .non_account_alt_rw_zones = []
473485 self .master_zone = None
474486
475487 for z in zonegroup .zones :
476488 zone_conn = z .get_conn (user .credentials )
489+ non_account_zone_conn = z .get_conn (non_account_user .credentials )
490+ non_account_alt_zone_conn = z .get_conn (non_account_alt_user .credentials )
477491 self .zones .append (zone_conn )
492+ self .non_account_zones .append (non_account_zone_conn )
493+ self .non_account_alt_zones .append (non_account_alt_zone_conn )
478494 if z .is_read_only ():
479495 self .ro_zones .append (zone_conn )
496+ self .non_account_ro_zones .append (non_account_zone_conn )
497+ self .non_account_alt_ro_zones .append (non_account_alt_zone_conn )
480498 else :
481499 self .rw_zones .append (zone_conn )
500+ self .non_account_rw_zones .append (non_account_zone_conn )
501+ self .non_account_alt_rw_zones .append (non_account_alt_zone_conn )
482502
483503 if z == zonegroup .master_zone :
484504 self .master_zone = zone_conn
@@ -3703,3 +3723,165 @@ def test_bucket_create_location_constraint():
37033723 Bucket = bucket_name ,
37043724 CreateBucketConfiguration = {'LocationConstraint' : zg .name })
37053725 assert e .response ['ResponseMetadata' ]['HTTPStatusCode' ] == 400
3726+
3727+ def allow_bucket_replication (function ):
3728+ def wrapper (* args , ** kwargs ):
3729+ zonegroup = realm .master_zonegroup ()
3730+ if len (zonegroup .zones ) < 2 :
3731+ raise SkipTest ("More than one zone needed in any one or multiple zone(s)." )
3732+
3733+ zones = "," .join ([z .name for z in zonegroup .zones ])
3734+ z = zonegroup .zones [0 ]
3735+ c = z .cluster
3736+
3737+ create_sync_policy_group (c , "sync-group" )
3738+ create_sync_group_flow_symmetrical (c , "sync-group" , "sync-flow" , zones )
3739+ create_sync_group_pipe (c , "sync-group" , "sync-pipe" , zones , zones )
3740+
3741+ zonegroup .period .update (z , commit = True )
3742+ realm_meta_checkpoint (realm )
3743+
3744+ try :
3745+ function (* args , ** kwargs )
3746+ finally :
3747+ remove_sync_group_pipe (c , "sync-group" , "sync-pipe" )
3748+ remove_sync_group_flow_symmetrical (c , "sync-group" , "sync-flow" )
3749+ remove_sync_policy_group (c , "sync-group" )
3750+
3751+ zonegroup .period .update (z , commit = True )
3752+ realm_meta_checkpoint (realm )
3753+
3754+ return wrapper
3755+
3756+ @allow_bucket_replication
3757+ def test_bucket_replication_normal ():
3758+ zonegroup = realm .master_zonegroup ()
3759+ zonegroup_conns = ZonegroupConns (zonegroup )
3760+
3761+ source = zonegroup_conns .non_account_rw_zones [0 ]
3762+ dest = zonegroup_conns .non_account_rw_zones [1 ]
3763+
3764+ source_bucket = source .create_bucket (gen_bucket_name ())
3765+ dest_bucket = dest .create_bucket (gen_bucket_name ())
3766+ zonegroup_meta_checkpoint (zonegroup )
3767+
3768+ # create replication configuration
3769+ response = source .s3_client .put_bucket_replication (
3770+ Bucket = source_bucket .name ,
3771+ ReplicationConfiguration = {
3772+ 'Role' : '' ,
3773+ 'Rules' : [{
3774+ 'ID' : 'rule1' ,
3775+ 'Status' : 'Enabled' ,
3776+ 'Destination' : {
3777+ 'Bucket' : f'arn:aws:s3:::{ dest_bucket .name } ' ,
3778+ }
3779+ }]
3780+ }
3781+ )
3782+ assert response ['ResponseMetadata' ]['HTTPStatusCode' ] == 200
3783+ zonegroup_meta_checkpoint (zonegroup )
3784+
3785+ # upload an object and wait for sync.
3786+ objname = 'dummy'
3787+ k = new_key (source , source_bucket , objname )
3788+ k .set_contents_from_string ('foo' )
3789+ zone_data_checkpoint (dest .zone , source .zone )
3790+
3791+ # check that object exists in destination bucket
3792+ k = get_key (dest , dest_bucket , objname )
3793+ assert_equal (k .get_contents_as_string ().decode ('utf-8' ), 'foo' )
3794+
3795+ @allow_bucket_replication
3796+ def test_bucket_replication_alt_user_forbidden ():
3797+ zonegroup = realm .master_zonegroup ()
3798+ zonegroup_conns = ZonegroupConns (zonegroup )
3799+
3800+ source = zonegroup_conns .non_account_rw_zones [0 ]
3801+ dest = zonegroup_conns .non_account_alt_rw_zones [1 ]
3802+
3803+ source_bucket = source .create_bucket (gen_bucket_name ())
3804+ dest_bucket = dest .create_bucket (gen_bucket_name ())
3805+ zonegroup_meta_checkpoint (zonegroup )
3806+
3807+ # create replication configuration
3808+ response = source .s3_client .put_bucket_replication (
3809+ Bucket = source_bucket .name ,
3810+ ReplicationConfiguration = {
3811+ 'Role' : '' ,
3812+ 'Rules' : [{
3813+ 'ID' : 'rule1' ,
3814+ 'Status' : 'Enabled' ,
3815+ 'Destination' : {
3816+ 'Bucket' : f'arn:aws:s3:::{ dest_bucket .name } ' ,
3817+ }
3818+ }]
3819+ }
3820+ )
3821+ assert response ['ResponseMetadata' ]['HTTPStatusCode' ] == 200
3822+ zonegroup_meta_checkpoint (zonegroup )
3823+
3824+ # upload an object and wait for sync.
3825+ objname = 'dummy'
3826+ k = new_key (source , source_bucket , objname )
3827+ k .set_contents_from_string ('foo' )
3828+ zone_data_checkpoint (dest .zone , source .zone )
3829+
3830+ # check that object does not exist in destination bucket
3831+ e = assert_raises (ClientError , dest .s3_client .get_object , Bucket = dest_bucket .name , Key = objname )
3832+ assert e .response ['Error' ]['Code' ] == 'NoSuchKey'
3833+
3834+ @allow_bucket_replication
3835+ def test_bucket_replication_alt_user ():
3836+ zonegroup = realm .master_zonegroup ()
3837+ zonegroup_conns = ZonegroupConns (zonegroup )
3838+
3839+ source = zonegroup_conns .non_account_rw_zones [0 ]
3840+ dest = zonegroup_conns .non_account_alt_rw_zones [1 ]
3841+
3842+ source_bucket = source .create_bucket (gen_bucket_name ())
3843+ dest_bucket = dest .create_bucket (gen_bucket_name ())
3844+ zonegroup_meta_checkpoint (zonegroup )
3845+
3846+ # create replication configuration
3847+ response = source .s3_client .put_bucket_replication (
3848+ Bucket = source_bucket .name ,
3849+ ReplicationConfiguration = {
3850+ 'Role' : '' ,
3851+ 'Rules' : [{
3852+ 'ID' : 'rule1' ,
3853+ 'Status' : 'Enabled' ,
3854+ 'Destination' : {
3855+ 'Bucket' : f'arn:aws:s3:::{ dest_bucket .name } ' ,
3856+ 'AccessControlTranslation' : {
3857+ 'Owner' : non_account_alt_user .id ,
3858+ },
3859+ }
3860+ }]
3861+ }
3862+ )
3863+ assert response ['ResponseMetadata' ]['HTTPStatusCode' ] == 200
3864+ # give access to user to write to alt user's bucket
3865+ dest .s3_client .put_bucket_policy (
3866+ Bucket = dest_bucket .name ,
3867+ Policy = json .dumps ({
3868+ 'Version' : '2012-10-17' ,
3869+ 'Statement' : [{
3870+ 'Effect' : 'Allow' ,
3871+ 'Principal' : {'AWS' : [f"arn:aws:iam:::user/{ user .id } " ]},
3872+ 'Action' : 's3:PutObject' ,
3873+ 'Resource' : f'arn:aws:s3:::{ dest_bucket .name } /*' ,
3874+ }]
3875+ })
3876+ )
3877+ zonegroup_meta_checkpoint (zonegroup )
3878+
3879+ # upload an object and wait for sync.
3880+ objname = 'dummy'
3881+ k = new_key (source , source_bucket , objname )
3882+ k .set_contents_from_string ('foo' )
3883+ zone_data_checkpoint (dest .zone , source .zone )
3884+
3885+ # check that object exists in destination bucket
3886+ k = get_key (dest , dest_bucket , objname )
3887+ assert_equal (k .get_contents_as_string ().decode ('utf-8' ), 'foo' )
0 commit comments