class TestCachedConfiguration(ShotgunTestBase): def setUp(self): super(TestCachedConfiguration, self).setUp() # Reset the tank_name and create a storage named after the one in the config. self.mockgun.update("Project", self.project["id"], {"tank_name": None}) self.mockgun.create("LocalStorage", {"code": "primary"}) # Initialize a cached configuration pointing to the config. config_root = os.path.join(self.fixtures_root, "bootstrap_tests", "config") self._temp_config_root = os.path.join(self.tank_temp, self.short_test_name) self._cached_config = CachedConfiguration( sgtk.util.ShotgunPath.from_current_os_path(self._temp_config_root), self.mockgun, sgtk.descriptor.create_descriptor( self.mockgun, sgtk.descriptor.Descriptor.CONFIG, "sgtk:descriptor:path?path={0}".format(config_root), ), self.project["id"], "basic.*", None, [], ) # Due to this being a test that runs offline, we can't use anything other than a # path descriptor, which means that it is mutable. Because LOCAL_CFG_DIFFERENT # is actually returned by three different code paths, the only way to ensure that # we are indeed in the up to date state, which means everything is ready to do, is # to cheat and make the descriptor immutable by monkey-patching it. self._cached_config._descriptor.is_immutable = lambda: True # Seems up the test tremendously since installing core becomes a noop. self._cached_config._config_writer.install_core = lambda _: None self._cached_config._config_writer.create_tank_command = lambda: None def test_verifies_tank_name(self): """ Ensures that missing tank name on project is detected when using roots. """ # Make sure that the missing tank name is detected. with self.assertRaises(sgtk.bootstrap.TankMissingTankNameError): self._cached_config.verify_required_shotgun_fields() # Ensure our change is backwards compatible. with self.assertRaises(sgtk.bootstrap.TankBootstrapError): self._cached_config.verify_required_shotgun_fields() def test_ensure_config_not_missing_after_update(self): """ Ensures once a configuration is written that is ready. """ self.assertEqual(self._cached_config.status(), self._cached_config.LOCAL_CFG_MISSING) self._cached_config.update_configuration() self.assertEqual(self._cached_config.status(), self._cached_config.LOCAL_CFG_UP_TO_DATE) def test_ensure_config_half_written_is_invalid(self): """ Ensures a failure during bootstrap is detected and renders the configuration invalid. """ self.assertEqual(self._cached_config.status(), self._cached_config.LOCAL_CFG_MISSING) # Force put the configuration in an inconsistent state. self._cached_config._config_writer.start_transaction() # Create the config folder so it isn't barely missing. os.makedirs(os.path.join(self._temp_config_root, "config")) self.assertEqual(self._cached_config.status(), self._cached_config.LOCAL_CFG_INVALID) def test_missing_deployment_file(self): """ Ensures a missing deployment file results in an invalid config status. """ self._cached_config.update_configuration() # Using a path descriptor will always give different. self.assertEqual(self._cached_config.status(), self._cached_config.LOCAL_CFG_UP_TO_DATE) os.remove( self._cached_config._config_writer.get_descriptor_metadata_file()) self.assertEqual(self._cached_config.status(), self._cached_config.LOCAL_CFG_INVALID) def test_generation_number_mismatch(self): """ Ensures a generation number mismatch will generate a different config status. """ self._cached_config.update_configuration() # Using a path descriptor will always give different. self.assertEqual(self._cached_config.status(), self._cached_config.LOCAL_CFG_UP_TO_DATE) self._update_deploy_file(generation=9999999) self.assertEqual(self._cached_config.status(), self._cached_config.LOCAL_CFG_DIFFERENT) def test_different_descriptor(self): """ Ensures a descriptor mismatch will generate a different config status. """ self._cached_config.update_configuration() # Using a path descriptor will always give different. self.assertEqual(self._cached_config.status(), self._cached_config.LOCAL_CFG_UP_TO_DATE) self._update_deploy_file(descriptor={ "type": "app_store", "name": "tk-config-basic", "version": "v1.0.0", }) self.assertEqual(self._cached_config.status(), self._cached_config.LOCAL_CFG_DIFFERENT) def test_corrupted_file(self): """ Ensures a corrupted deploy file will generates an invalid status. """ self._cached_config.update_configuration() # Using a path descriptor will always give different. self.assertEqual(self._cached_config.status(), self._cached_config.LOCAL_CFG_UP_TO_DATE) self._update_deploy_file(corrupt=True) self.assertEqual(self._cached_config.status(), self._cached_config.LOCAL_CFG_INVALID) def test_mutable_descriptors(self): """ Ensures a mutable descriptor will yield a different config status. """ self._cached_config.update_configuration() self.assertEqual(self._cached_config.status(), self._cached_config.LOCAL_CFG_UP_TO_DATE) # Now force the descriptor to report that is is not immutable, the status will then be considered # as different since we can't assume everything has stayed the same. self._cached_config._descriptor.is_immutable = lambda: False self.assertEqual(self._cached_config.status(), self._cached_config.LOCAL_CFG_DIFFERENT) def _update_deploy_file(self, generation=None, descriptor=None, corrupt=False): """ Updates the deploy file. :param generation: If set, will update the generation number of the config. :param descriptor: If set, will update the descriptor of the config. :param corrupt: If set, will corrupt the configuration file. """ path = self._cached_config._config_writer.get_descriptor_metadata_file( ) if corrupt: data = "corrupted" else: with open(path, "rt") as fh: data = yaml.load(fh, Loader=yaml.FullLoader) if generation is not None: data["deploy_generation"] = generation if descriptor is not None: data["config_descriptor"] = descriptor with open(path, "wt") as fh: yaml.dump(data, fh)
class TestSSOClaims(TestConfigurationBase): def setUp(self): super(TestSSOClaims, self).setUp() config_folder = sgtk.util.ShotgunPath.from_current_os_path( os.path.join(self.tank_temp, str(uuid.uuid4())) ) self._configuration = CachedConfiguration( config_folder, self.mockgun, sgtk.descriptor.create_descriptor( self.mockgun, sgtk.descriptor.Descriptor.CONFIG, "sgtk:descriptor:path?path={0}".format(self.fixtures_root) ), self.project["id"], "basic.dcc", None, [] ) self._mock_return_value( "tank.pipelineconfig_utils.get_core_python_path_for_config", return_value=os.path.join(REPO_ROOT, "python") ) # Do not waste time copying files around or core swapping. Also, deactivate # thread startup and shutdown, we only want to ensure they are invoked. for mocked_method in [ "tank.bootstrap.import_handler.CoreImportHandler.swap_core", "tank.bootstrap.configuration_writer.ConfigurationWriter.install_core", "tank.bootstrap.configuration_writer.ConfigurationWriter.create_tank_command", ]: self._mock_return_value(mocked_method, return_value=None) self._start_claims_mock = self._mock_return_value( "tank.authentication.user.ShotgunSamlUser.start_claims_renewal", return_value=None ) self._stop_claims_mock = self._mock_return_value( "tank.authentication.user.ShotgunSamlUser.stop_claims_renewal", return_value=None ) def test_claims_renewal_inactive(self): """ Checks that is the claims renewal loop is not running it will not be restarted after core swap. """ bootstrap_user = self._create_sso_user("bootstrap_user") project_user = self._create_sso_user("bootstrap_user") self._configuration.update_configuration() # Create a default user. with patch( "tank.authentication.ShotgunAuthenticator.get_default_user", return_value=project_user ): _, swapped_user = self._configuration.get_tk_instance(bootstrap_user) self.assertIsInstance(swapped_user, ShotgunSamlUser) self.assertEqual(self._start_claims_mock.called, False) self.assertEqual(self._stop_claims_mock.called, False) def test_claims_renewal_active(self): """ Checks that claims renewal is stopped and restarted. """ bootstrap_user = self._create_sso_user("bootstrap_user") project_user = self._create_sso_user("bootstrap_user") self._configuration.update_configuration() bootstrap_user.is_claims_renewal_active = lambda: True # Create a default user. with patch( "tank.authentication.ShotgunAuthenticator.get_default_user", return_value=project_user ): self.assertEqual(self._start_claims_mock.called, False) self.assertEqual(self._stop_claims_mock.called, False) _, swapped_user = self._configuration.get_tk_instance(bootstrap_user) self.assertIsInstance(swapped_user, ShotgunSamlUser) self.assertEqual(self._start_claims_mock.called, True) self.assertEqual(self._stop_claims_mock.called, True) def test_claims_to_script(self): """ Checks that claims renewal is stopped and restarted. """ bootstrap_user = self._create_sso_user("bootstrap_user") script_user = self._create_script_user("script_user") self._configuration.update_configuration() bootstrap_user.is_claims_renewal_active = lambda: True # Create a default user. with patch( "tank.authentication.ShotgunAuthenticator.get_default_user", return_value=script_user ): self.assertEqual(self._start_claims_mock.called, False) self.assertEqual(self._stop_claims_mock.called, False) _, swapped_user = self._configuration.get_tk_instance(bootstrap_user) self.assertEqual(self._stop_claims_mock.called, True) self.assertEqual(self._start_claims_mock.called, False) self.assertIsNone(swapped_user.login)
class TestCachedConfiguration(ShotgunTestBase): def setUp(self): super(TestCachedConfiguration, self).setUp() # Reset the tank_name and create a storage named after the one in the config. self.mockgun.update("Project", self.project["id"], {"tank_name": None}) self.mockgun.create("LocalStorage", {"code": "primary"}) # Initialize a cached configuration pointing to the config. config_root = os.path.join(self.fixtures_root, "bootstrap_tests", "config") self._temp_config_root = os.path.join(self.tank_temp, self.short_test_name) self._cached_config = CachedConfiguration( sgtk.util.ShotgunPath.from_current_os_path(self._temp_config_root), self.mockgun, sgtk.descriptor.create_descriptor( self.mockgun, sgtk.descriptor.Descriptor.CONFIG, "sgtk:descriptor:path?path={0}".format(config_root) ), self.project["id"], "basic.*", None, [] ) # Due to this being a test that runs offline, we can't use anything other than a # path descriptor, which means that it is mutable. Because LOCAL_CFG_DIFFERENT # is actually returned by three different code paths, the only way to ensure that # we are indeed in the up to date state, which means everything is ready to do, is # to cheat and make the descriptor immutable by monkey-patching it. self._cached_config._descriptor.is_immutable = lambda: True # Seems up the test tremendously since installing core becomes a noop. self._cached_config._config_writer.install_core = lambda _: None self._cached_config._config_writer.create_tank_command = lambda: None def test_verifies_tank_name(self): """ Ensures that missing tank name on project is detected when using roots. """ # Make sure that the missing tank name is detected. with self.assertRaises(sgtk.bootstrap.TankMissingTankNameError): self._cached_config.verify_required_shotgun_fields() # Ensure our change is backwards compatible. with self.assertRaises(sgtk.bootstrap.TankBootstrapError): self._cached_config.verify_required_shotgun_fields() def test_ensure_config_not_missing_after_update(self): """ Ensures once a configuration is written that is ready. """ self.assertEqual(self._cached_config.status(), self._cached_config.LOCAL_CFG_MISSING) self._cached_config.update_configuration() self.assertEqual(self._cached_config.status(), self._cached_config.LOCAL_CFG_UP_TO_DATE) def test_ensure_config_half_written_is_invalid(self): """ Ensures a failure during bootstrap is detected and renders the configuration invalid. """ self.assertEqual(self._cached_config.status(), self._cached_config.LOCAL_CFG_MISSING) # Force put the configuration in an inconsistent state. self._cached_config._config_writer.start_transaction() # Create the config folder so it isn't barely missing. os.makedirs(os.path.join(self._temp_config_root, "config")) self.assertEqual(self._cached_config.status(), self._cached_config.LOCAL_CFG_INVALID) def test_missing_deployment_file(self): """ Ensures a missing deployment file results in an invalid config status. """ self._cached_config.update_configuration() # Using a path descriptor will always give different. self.assertEqual(self._cached_config.status(), self._cached_config.LOCAL_CFG_UP_TO_DATE) os.remove(self._cached_config._config_writer.get_descriptor_metadata_file()) self.assertEqual(self._cached_config.status(), self._cached_config.LOCAL_CFG_INVALID) def test_generation_number_mismatch(self): """ Ensures a generation number mismatch will generate a different config status. """ self._cached_config.update_configuration() # Using a path descriptor will always give different. self.assertEqual(self._cached_config.status(), self._cached_config.LOCAL_CFG_UP_TO_DATE) self._update_deploy_file(generation=9999999) self.assertEqual(self._cached_config.status(), self._cached_config.LOCAL_CFG_DIFFERENT) def test_different_descriptor(self): """ Ensures a descriptor mismatch will generate a different config status. """ self._cached_config.update_configuration() # Using a path descriptor will always give different. self.assertEqual(self._cached_config.status(), self._cached_config.LOCAL_CFG_UP_TO_DATE) self._update_deploy_file(descriptor={"type": "app_store", "name": "tk-config-basic", "version": "v1.0.0"}) self.assertEqual(self._cached_config.status(), self._cached_config.LOCAL_CFG_DIFFERENT) def test_corrupted_file(self): """ Ensures a corrupted deploy file will generates an invalid status. """ self._cached_config.update_configuration() # Using a path descriptor will always give different. self.assertEqual(self._cached_config.status(), self._cached_config.LOCAL_CFG_UP_TO_DATE) self._update_deploy_file(corrupt=True) self.assertEqual(self._cached_config.status(), self._cached_config.LOCAL_CFG_INVALID) def test_mutable_descriptors(self): """ Ensures a mutable descriptor will yield a different config status. """ self._cached_config.update_configuration() self.assertEqual(self._cached_config.status(), self._cached_config.LOCAL_CFG_UP_TO_DATE) # Now force the descriptor to report that is is not immutable, the status will then be considered # as different since we can't assume everything has stayed the same. self._cached_config._descriptor.is_immutable = lambda: False self.assertEqual(self._cached_config.status(), self._cached_config.LOCAL_CFG_DIFFERENT) def _update_deploy_file(self, generation=None, descriptor=None, corrupt=False): """ Updates the deploy file. :param generation: If set, will update the generation number of the config. :param descriptor: If set, will update the descriptor of the config. :param corrupt: If set, will corrupt the configuration file. """ path = self._cached_config._config_writer.get_descriptor_metadata_file() if corrupt: data = "corrupted" else: with open(path, "rt") as fh: data = yaml.load(fh) if generation is not None: data["deploy_generation"] = generation if descriptor is not None: data["config_descriptor"] = descriptor with open(path, "wt") as fh: yaml.dump(data, fh)