예제 #1
0
    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)
예제 #2
0
    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
예제 #3
0
    def test_verifies_tank_name(self):
        """
        Ensures that missing tank name on project is detected when using roots.
        """

        # 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")
        cached_config = CachedConfiguration(
            self.tank_temp,
            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,
            []
        )

        # Make sure that the missing tank name is detected.
        with self.assertRaises(sgtk.bootstrap.TankMissingTankNameError):
            cached_config.verify_required_shotgun_fields()

        # Ensure our change is backwards compatible.
        with self.assertRaises(sgtk.bootstrap.TankBootstrapError):
            cached_config.verify_required_shotgun_fields()
예제 #4
0
    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.cached_configuration.CachedConfiguration._ensure_core_local",
            "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
        )
예제 #5
0
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)
예제 #6
0
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)
예제 #7
0
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)