@@ -834,59 +834,62 @@ def do_test_redirect_in_data_object_put_and_get__issue_452(self, content, data_c
834834 self .skipTest ('Expects iRODS server version 4.3.1' )
835835 LOCAL_FILE = mktemp ()
836836 filename = ''
837- try :
838- with self .create_simple_resc (hostname = 'localhost' ) as rescName :
839- with NamedTemporaryFile (delete = False ) as f :
840- filename = f .name
841- f .write (content )
842- data_ctx ['initialize' ]()
843- sess = data_ctx ['session' ]
844- remote_name = data_ctx ['path' ]
845- PUT_LOG = self .In_Memory_Stream (always_unicode = True )
846- with helpers .enableLogging (logging .getLogger ('irods.manager.data_object_manager' ),
847- logging .StreamHandler , (PUT_LOG ,), level_ = logging .DEBUG ),\
848- helpers .enableLogging (logging .getLogger ('irods.parallel' ),
849- logging .StreamHandler , (PUT_LOG ,), level_ = logging .DEBUG ):
850- sess .data_objects .put (filename , remote_name , ** {kw .DEST_RESC_NAME_KW : rescName })
851- def srch (BUF ):
852- nthr = 0
853- search_text = BUF .getvalue ()
854- find_iterator = itertools .chain ( re .finditer (u'redirect_to_host = (\S+)' , search_text ),
855- re .finditer (u'target_host = (\S+)' , search_text ) )
856- for match in find_iterator :
857- nthr += 1
858- self .assertEqual (match .group (1 ), u'localhost' )
859- occur_threshold = (1 if len (content ) <= 32 * MEBI else 2 )
860- self .assertGreaterEqual (nthr , occur_threshold )
861- srch (PUT_LOG )
862- generator = None
863- # Activate a read ticket on a new session if necessary, and attempt a GET
864- if data_ctx ['ticket_access' ]:
865- for access in iRODSAccess ('own' ,remote_name ,self .sess .username ), \
866- iRODSAccess ('null' ,remote_name ,sess .username ):
867- self .sess .acls .set (access , admin = True )
868- generator = self ._data_object_and_associated_ticket (data_name = remote_name , auto_delete_data = False , ticket_access = 'read' )
869- # Emulate the 'with'-block construction for the read ticket:
870- data_ctx_get = next (generator )
871- data_ctx_get ['initialize' ]()
872- sess = data_ctx_get ['session' ]
873- GET_LOG = self .In_Memory_Stream (always_unicode = True )
874- with helpers .enableLogging (logging .getLogger ('irods.manager.data_object_manager' ),
875- logging .StreamHandler , (GET_LOG ,), level_ = logging .DEBUG ),\
876- helpers .enableLogging (logging .getLogger ('irods.parallel' ),
877- logging .StreamHandler , (GET_LOG ,), level_ = logging .DEBUG ):
878- sess .data_objects .get (remote_name ,LOCAL_FILE )
879- srch (GET_LOG )
880- with open (LOCAL_FILE ,'rb' ) as get_result :
881- self .assertTrue (content , get_result .read ())
882- # Finalize the emulated 'with'-block construction for the read ticket, if active:
883- del generator
884- data_ctx ['finalize' ]()
885- finally :
886- if os .path .isfile (LOCAL_FILE ):
887- os .unlink (LOCAL_FILE )
888- if filename :
889- os .unlink (filename )
837+ with config .loadlines (entries = [dict (setting = 'data_objects.allow_redirect' ,value = True )]):
838+ try :
839+ with self .create_simple_resc (hostname = 'localhost' ) as rescName :
840+ with NamedTemporaryFile (delete = False ) as f :
841+ filename = f .name
842+ f .write (content )
843+ data_ctx ['initialize' ]()
844+ sess = data_ctx ['session' ]
845+ remote_name = data_ctx ['path' ]
846+ PUT_LOG = self .In_Memory_Stream (always_unicode = True )
847+ with helpers .enableLogging (logging .getLogger ('irods.manager.data_object_manager' ),
848+ logging .StreamHandler , (PUT_LOG ,), level_ = logging .DEBUG ),\
849+ helpers .enableLogging (logging .getLogger ('irods.parallel' ),
850+ logging .StreamHandler , (PUT_LOG ,), level_ = logging .DEBUG ):
851+ sess .data_objects .put (filename , remote_name , ** {kw .DEST_RESC_NAME_KW : rescName })
852+ # Within a buffer 'BUF' (is expected to be an io.StringIO object) assert the presence of certain
853+ # log text that will indicate a redirection was performed.
854+ def assert_expected_redirection_logging (BUF ):
855+ nthr = 0
856+ search_text = BUF .getvalue ()
857+ find_iterator = itertools .chain ( re .finditer (u'redirect_to_host = (\S+)' , search_text ),
858+ re .finditer (u'target_host = (\S+)' , search_text ) )
859+ for match in find_iterator :
860+ nthr += 1
861+ self .assertEqual (match .group (1 ), u'localhost' )
862+ occur_threshold = (1 if len (content ) <= 32 * MEBI else 2 )
863+ self .assertGreaterEqual (nthr , occur_threshold )
864+ assert_expected_redirection_logging (PUT_LOG )
865+ generator = None
866+ # Activate a read ticket on a new session if necessary, and attempt a GET
867+ if data_ctx ['ticket_access' ]:
868+ for access in iRODSAccess ('own' ,remote_name ,self .sess .username ), \
869+ iRODSAccess ('null' ,remote_name ,sess .username ):
870+ self .sess .acls .set (access , admin = True )
871+ generator = self ._data_object_and_associated_ticket (data_name = remote_name , auto_delete_data = False , ticket_access = 'read' )
872+ # Emulate the 'with'-block construction for the read ticket:
873+ data_ctx_get = next (generator )
874+ data_ctx_get ['initialize' ]()
875+ sess = data_ctx_get ['session' ]
876+ GET_LOG = self .In_Memory_Stream (always_unicode = True )
877+ with helpers .enableLogging (logging .getLogger ('irods.manager.data_object_manager' ),
878+ logging .StreamHandler , (GET_LOG ,), level_ = logging .DEBUG ),\
879+ helpers .enableLogging (logging .getLogger ('irods.parallel' ),
880+ logging .StreamHandler , (GET_LOG ,), level_ = logging .DEBUG ):
881+ sess .data_objects .get (remote_name ,LOCAL_FILE )
882+ assert_expected_redirection_logging (GET_LOG )
883+ with open (LOCAL_FILE ,'rb' ) as get_result :
884+ self .assertTrue (content , get_result .read ())
885+ # Finalize the emulated 'with'-block construction for the read ticket, if active:
886+ del generator
887+ data_ctx ['finalize' ]()
888+ finally :
889+ if os .path .isfile (LOCAL_FILE ):
890+ os .unlink (LOCAL_FILE )
891+ if filename :
892+ os .unlink (filename )
890893
891894 def test_redirect_in_data_object_open__issue_452 (self ):
892895 self ._skip_unless_connected_to_local_computer_by_other_than_localhost_synonym ()
@@ -895,18 +898,19 @@ def test_redirect_in_data_object_open__issue_452(self):
895898 sess = self .sess
896899 home = helpers .home_collection (sess )
897900
898- with self .create_simple_resc (hostname = 'localhost' ) as rescName :
899- try :
900- test_path = home + '/data_open_452'
901- desc = sess .data_objects .open (test_path , 'w' , ** {kw .RESC_NAME_KW : rescName })
902- self .assertEqual ('localhost' , desc .raw .session .host )
903- desc .close ()
904- desc = sess .data_objects .open (test_path , 'r' )
905- self .assertEqual ('localhost' , desc .raw .session .host )
906- desc .close ()
907- finally :
908- if sess .data_objects .exists (test_path ):
909- sess .data_objects .unlink (test_path , force = True )
901+ with config .loadlines (entries = [dict (setting = 'data_objects.allow_redirect' ,value = True )]):
902+ with self .create_simple_resc (hostname = 'localhost' ) as rescName :
903+ try :
904+ test_path = home + '/data_open_452'
905+ desc = sess .data_objects .open (test_path , 'w' , ** {kw .RESC_NAME_KW : rescName })
906+ self .assertEqual ('localhost' , desc .raw .session .host )
907+ desc .close ()
908+ desc = sess .data_objects .open (test_path , 'r' )
909+ self .assertEqual ('localhost' , desc .raw .session .host )
910+ desc .close ()
911+ finally :
912+ if sess .data_objects .exists (test_path ):
913+ sess .data_objects .unlink (test_path , force = True )
910914
911915
912916 def test_create_with_checksum (self ):
@@ -2174,35 +2178,53 @@ def test_touch_operation_does_not_work_when_given_a_collection__525(self):
21742178 with self .assertRaises (ex .InvalidInputArgument ):
21752179 user_session .data_objects .touch (home_collection_path )
21762180
2181+ def assert_redirect_happens_on_open (self , open_opts ):
2182+ name = 'redirect_happens_' + unique_name (my_function_name (), datetime .now ())
2183+ data_path = '{self.coll_path}/{name}' .format (** locals ())
2184+ try :
2185+ PUT_LOG = self .In_Memory_Stream (always_unicode = True )
2186+ with helpers .enableLogging (logging .getLogger ('irods.manager.data_object_manager' ),
2187+ logging .StreamHandler , (PUT_LOG ,), level_ = logging .DEBUG ):
2188+ with self .sess .data_objects .open (data_path ,'w' ,** open_opts ):
2189+ pass
2190+ log_text = PUT_LOG .getvalue ()
2191+ self .assertIn ('redirect_to_host' ,log_text )
2192+ finally :
2193+ if self .sess .data_objects .exists (data_path ):
2194+ self .sess .data_objects .unlink (data_path , force = True )
2195+
21772196 @unittest .skipIf (six .PY2 , "Python2 won't destruct an out-of-scope iRODSSession due to lazy GC ref-cycle detection." )
21782197 def test_client_redirect_lets_go_of_connections__issue_562 (self ):
21792198 self ._skip_unless_connected_to_local_computer_by_other_than_localhost_synonym ()
21802199 # Force data object connections to redirect by enforcing a non-equivalent hostname for their resource
21812200 total_conns = lambda session : len (session .pool .idle | session .pool .active )
2182- with self .create_simple_resc (hostname = 'localhost' ) as resc_name :
2183- # A reasonable number of data objects to create without eliciting problems.
2184- # (But before resolution of #562, a NetworkException was eventually thrown from
2185- # this test loop if a session cleanup() did not intervene between open() calls.)
2186- REPS_TO_REPRODUCE_CONNECT_ERROR = 100
2187- paths = []
2188- prev_conns = None
2189- try :
2190- # Try to exhaust connections
2191- for n in range (REPS_TO_REPRODUCE_CONNECT_ERROR ):
2192- data_path = '{self.coll_path}/issue_562_test_obj_{n:03d}.dat' .format (** locals ())
2193- paths .append (data_path )
2194- with self .sess .data_objects .open (data_path , 'w' , ** {kw .DEST_RESC_NAME_KW : resc_name }) as f :
2195- pass
2196- # Assert number of connections does not increase
2197- current_conns = total_conns (self .sess )
2198- if isinstance (prev_conns ,int ):
2199- self .assertLessEqual (current_conns , prev_conns )
2200- prev_conns = current_conns
2201- finally :
2202- # Clean up data objects before resource is deleted.
2203- for data_path in paths :
2204- if self .sess .data_objects .exists (data_path ):
2205- self .sess .data_objects .unlink (data_path , force = True )
2201+ with config .loadlines (entries = [dict (setting = 'data_objects.allow_redirect' ,value = True )]):
2202+ with self .create_simple_resc (hostname = 'localhost' ) as resc_name :
2203+ self .assert_redirect_happens_on_open ( {kw .DEST_RESC_NAME_KW : resc_name } )
2204+ # A reasonable number of data objects to create without eliciting problems.
2205+ # (But before resolution of #562, a NetworkException was eventually thrown from
2206+ # this test loop if a session cleanup() did not intervene between open() calls.)
2207+ REPS_TO_REPRODUCE_CONNECT_ERROR = 100
2208+ paths = []
2209+ prev_conns = None
2210+ try :
2211+ # Try to exhaust connections
2212+ for n in range (REPS_TO_REPRODUCE_CONNECT_ERROR ):
2213+ data_path = '{self.coll_path}/issue_562_test_obj_{n:03d}.dat' .format (** locals ())
2214+ paths .append (data_path )
2215+ with self .sess .data_objects .open (data_path , 'w' , ** {kw .DEST_RESC_NAME_KW : resc_name }) as f :
2216+ pass
2217+ # Assert number of connections does not increase
2218+ current_conns = total_conns (self .sess )
2219+ if isinstance (prev_conns ,int ):
2220+ self .assertLessEqual (current_conns , prev_conns )
2221+ prev_conns = current_conns
2222+ finally :
2223+ # Clean up data objects before resource is deleted.
2224+ for data_path in paths :
2225+ if self .sess .data_objects .exists (data_path ):
2226+ self .sess .data_objects .unlink (data_path , force = True )
2227+
22062228
22072229 @unittest .skipIf (progressbar is None , "progressbar is not installed" )
22082230 def test_progressbar_style_of_pbar_without_registering__issue_574 (self ):
@@ -2397,6 +2419,34 @@ def test_replica_truncate_related_errors__issue_534(self):
23972419 if data_objs .exists (data_path ):
23982420 data_objs .unlink (data_path , force = True )
23992421
2422+ def test_allow_redirect_configuration_setting__issue_627 (self ):
2423+
2424+ self ._skip_unless_connected_to_local_computer_by_other_than_localhost_synonym ()
2425+
2426+ logical_paths = ['{}/issue_627_{}_{}' .format (self .coll_path , n , unique_name (my_function_name (), datetime .now ())) for n in range (2 )]
2427+
2428+ with self .create_simple_resc (hostname = 'localhost' ) as newResc1 ,\
2429+ self .create_simple_resc () as newResc2 :
2430+
2431+ if self .sess .resources .get (newResc1 ).location == self .sess .resources .get (newResc2 ).location :
2432+ self .skipTest ('test runs only if host locations differ between experimental and control resource' )
2433+
2434+ for use_redirect in (True ,False ):
2435+
2436+ with config .loadlines (entries = [dict (setting = 'data_objects.allow_redirect' ,value = use_redirect )]):
2437+
2438+ try :
2439+ with self .sess .data_objects .open (logical_paths [0 ], 'w' , ** {kw .RESC_NAME_KW :newResc1 }) as d1 ,\
2440+ self .sess .data_objects .open (logical_paths [1 ], 'w' , ** {kw .RESC_NAME_KW :newResc2 }) as d2 :
2441+
2442+ hostname_inequality_relation = (d1 .raw .session .host != d2 .raw .session .host )
2443+ self .assertEqual (use_redirect , hostname_inequality_relation )
2444+ finally :
2445+ for path in logical_paths :
2446+ if self .sess .data_objects .exists (path ):
2447+ self .sess .data_objects .unlink (path , force = True )
2448+
2449+
24002450 def test_replica_truncate__issue_534 (self ):
24012451 sess = self .sess
24022452 data_objs = self .sess .data_objects
0 commit comments