Пример #1
0
 def __init__(self, kube_config_file):
     super(OperatorInstaller, self).__init__(kube_config_file=kube_config_file)
     self.og_obj = OperatorGroup(kube_config_file=self.kube_config_file)
     self.sub_obj = Subscription(kube_config_file=self.kube_config_file)
     self.os_obj = OperatorSource(kube_config_file=self.kube_config_file)
     self.csc_obj = CatalogSourceConfig(kube_config_file=self.kube_config_file)
     self.cs_obj = CatalogSource(kube_config_file=self.kube_config_file)
     self.ohp_obj = OperatorhubPackages(kube_config_file=self.kube_config_file)
     self.proj_obj = OcpProjects(kube_config_file=self.kube_config_file)
 def __init__(self, kube_config_file: Optional[str] = None):
     super(OperatorInstaller,
           self).__init__(kube_config_file=kube_config_file)
     self.og_obj = OperatorGroup(kube_config_file=self.kube_config_file)
     self.sub_obj = Subscription(kube_config_file=self.kube_config_file)
     self.ohp_obj = OperatorhubPackages(
         kube_config_file=self.kube_config_file)
     self.proj_obj = OcpProjects(kube_config_file=self.kube_config_file)
     self.csv = ClusterServiceVersion(self.kube_config_file)
 def __init__(self):
     self.op_hub_obj = OperatorhubPackages(kube_config_file=get_kubeconfig)
     self.sub_obj = Subscription(kube_config_file=get_kubeconfig)
     self.og_obj = OperatorGroup(kube_config_file=get_kubeconfig)
     self.csv_obj = ClusterServiceVersion(kube_config_file=get_kubeconfig)
     self.project_obj = OcpProjects(kube_config_file=get_kubeconfig)
     self.oi_obj = OperatorInstaller(kube_config_file=get_kubeconfig)
     self.cs_obj = CatalogSource(kube_config_file=get_kubeconfig)
Пример #4
0
class OperatorInstaller(OcpBase):
    def __init__(self, kube_config_file):
        super(OperatorInstaller, self).__init__(kube_config_file=kube_config_file)
        self.og_obj = OperatorGroup(kube_config_file=self.kube_config_file)
        self.sub_obj = Subscription(kube_config_file=self.kube_config_file)
        self.os_obj = OperatorSource(kube_config_file=self.kube_config_file)
        self.csc_obj = CatalogSourceConfig(kube_config_file=self.kube_config_file)
        self.cs_obj = CatalogSource(kube_config_file=self.kube_config_file)
        self.ohp_obj = OperatorhubPackages(kube_config_file=self.kube_config_file)
        self.proj_obj = OcpProjects(kube_config_file=self.kube_config_file)

    def _source_processor(self, source):
        """
        Takes in a source as eihter a path to a JSON/YAML, or the soure in dict format.
        Currently supported are:
        For OCP4.1: CatalogSourceConfig and OperatorSource
        For OCP4.2: OperatorSource
        """
        def _source_path_processor(source_path):
            with open(source_path, 'r') as f:
                source = f.read()
            source_dict = dict()
            valid_source = True
            try:
                source_dict = yaml.safe_load(source)
                logger.debug("Successfully loaded the source file.")
            except ValueError as e:
                logger.error("Could not load the source file: {}".format(e))
                valid_source = False
            if valid_source:
                return _source_dict_processor(source_dict)
            else:
                err_msg = "The provided Json/Yaml source file could not be loaded"
                logger.exception(err_msg)
                raise TypeError(err_msg)

        def _source_dict_processor(source_dict):
            """
            A helper method that takes in a source object in dict form. We invoke the
            appropriate create method based on the resource kind and then we finally
            check that a catalogsource object has been created as a result.
            """
            resource_kind = source_dict['kind']
            logger.info("Operator source is of kind: {}".format(resource_kind))
            if resource_kind == 'CatalogSourceConfig':
                if self.version != ('4', '1'):
                    err_msg = "Source type CatalogSourceConfig is only supported for OCP version 4.1"
                    logger.exception(err_msg)
                    raise OcpUnsupportedVersion(err_msg)
                else:
                    resp = self.csc_obj.create_catalog_source_config(body=source_dict)
            elif resource_kind == 'OperatorSource':
                resp = self.os_obj.create_operator_source(body=source_dict)
            if resp.kind == 'CatalogSourceConfig':
                cs_namespace = resp.spec.targetNamespace
            else:
                cs_namespace = resp.metadata.namespace
            logger.info("Cheking if CatalogSource {} exists in namespace {}".format(resp.metadata.name, cs_namespace))
            assert self.cs_obj.is_catalog_source_present(resp.metadata.name, namespace=cs_namespace)
            cs = self.cs_obj.get_catalog_source(resp.metadata.name, namespace=cs_namespace)
            return cs.metadata.name, cs_namespace

        if isinstance(source, str):
            return _source_path_processor(source)
        elif isinstance(source, dict):
            return _source_dict_processor(source)

    def _derive_install_mode_from_target_namespaces(self, operator_name, target_namespaces):
        """
        Length of target_namespaces list enables us to derive the appropriate install mode
        Look at the length of the target_namespaces list:
            1. If it's zero, then we verify if 'AllNamespaces' is supported.
            2. If length is 1, then we verify if 'SingleNamespace' is supported.
            3. If length > 1, then we verify 'MultiNamespace' is supported.
        """
        target_namespaces_count = len(target_namespaces)
        if target_namespaces_count == 0:
            install_mode = 'AllNamespaces'
        elif target_namespaces_count == 1:
            install_mode = 'SingleNamespace'
        elif target_namespaces_count > 1:
            install_mode = 'MultiNamespace'
        return install_mode

    def _create_og(self, operator_name, install_mode, target_namespaces):
        """
        A helper method that creates a placeholder project that will contain the
        subscription and operator group objects necessary to install an operator
        For openshift 4.1, the operator installation only succeeds if these objects
        are installed in the opeshift-marketplace namespace. In openshift 4.2+, we
        create a project with the following naming convention:
        test + operator name + install mode + og-sub-project
        """
        if self.version == ('4', '1'):
            og_namespace = 'openshift-marketplace'
        else:
            og_name = operator_name + '-' + install_mode.lower() + '-og'
            og_namespace = 'test-' + operator_name + '-' + install_mode.lower() + '-og-sub-project'
        logger.info("Creating Project: {} that will hold the subscription and operator group".format(og_namespace))
        assert self.proj_obj.create_a_project(og_namespace)
        assert self.og_obj.create_operator_group(og_name, og_namespace, target_namespaces)
        return og_name, og_namespace

    def add_operator_to_cluster(self, operator_name, source=None, target_namespaces=[]):
        """
        Install an operator in a list of targeted namespaces
        :param operator_name: (required | str) The name of the operator to be installed
        :param source: (optional | str) The source of the operator to be installed. This parameter
                       can be in the form of a path to a source YAML or JSON, or it can also be
                       passed as a dictionary. If not specified, the package is assumed to be already
                       be visible throught the operator hub and so the source can be discovered.
        :param target_namespaces: (optional | list) A list of namespace/Projects where want the
                                   operator to be enabled in. If left unspecified, the operartor
                                   will be installed/enabled throughout the entire cluster.
        """
        if source:
            cs_name, cs_namespace = self._source_processor(source)
            if not self.ohp_obj.watch_package_manifest_present(operator_name):
                err_msg = "A package manifest for {} could not be found".format(operator_name)
                logger.exception(err_msg)
                raise ApiException(err_msg)
        else:
            pkg_obj = self.ohp_obj.get_package_manifest(operator_name)
            if pkg_obj:
                cs_name = pkg_obj.status.catalogSource
                cs_namespace = pkg_obj.metadata.namespace
            else:
                logger.exception(err_msg)
                raise ApiException(err_msg)
        install_mode = self._derive_install_mode_from_target_namespaces(operator_name, target_namespaces)
        og_name, og_namespace = self._create_og(operator_name, install_mode, target_namespaces)
        subscription = self.sub_obj.create_subscription(operator_name, install_mode, og_namespace)
        assert subscription.spec.source == cs_name
        assert subscription.spec.sourceNamespace == cs_namespace
        return True

    def delete_operator_from_cluster(self, operator_name):
        """
        Uninstall an operator from a cluster
        """
        pass
class OperatorInstaller(OcpBase):
    def __init__(self, kube_config_file: Optional[str] = None):
        super(OperatorInstaller,
              self).__init__(kube_config_file=kube_config_file)
        self.og_obj = OperatorGroup(kube_config_file=self.kube_config_file)
        self.sub_obj = Subscription(kube_config_file=self.kube_config_file)
        self.ohp_obj = OperatorhubPackages(
            kube_config_file=self.kube_config_file)
        self.proj_obj = OcpProjects(kube_config_file=self.kube_config_file)
        self.csv = ClusterServiceVersion(self.kube_config_file)

    def _derive_install_mode_from_target_namespaces(
            self, target_namespaces: Union[List[str], str]) -> str:
        """
        Length of target_namespaces list enables us to derive the appropriate install mode
        Look at the length of the target_namespaces list:
            1. If the wildcard '*' is used, it gets interpreted as'AllNamespaces'.
            2. If it's zero, we return 'OwnNamespace'.
            3. If length is 1, we return 'SingleNamespace'.
            4. If length > 1, we return 'MultiNamespace'.
        """
        if target_namespaces == '*':
            install_mode = "AllNamespaces"
        else:
            target_namespaces_count = len(target_namespaces)
            if target_namespaces_count == 0:
                install_mode = "OwnNamespace"
            elif target_namespaces_count == 1:
                install_mode = "SingleNamespace"
            elif target_namespaces_count > 1:
                install_mode = "MultiNamespace"
        return install_mode

    def _create_operator_namespace(self, operator_name: str,
                                   operator_namespace: str,
                                   channel_name: str) -> str:
        """
        A helper method that creates the namespace where both the subscription and operator
        group objects will be created to install an operator.
        """
        if not operator_namespace:
            operator_namespace = self.ohp_obj.get_channel_suggested_namespace(
                operator_name, channel_name)
        if not operator_namespace:
            operator_namespace = f"openshift-{operator_name}"
        assert self.proj_obj.create_a_namespace(operator_namespace)
        return operator_namespace

    def _create_og(self, operator_name: str, channel_name: str,
                   operator_namespace: str,
                   target_namespaces: Union[list, str]) -> Tuple[str, str]:
        """
        A helper method that creates the operator group in the generated operator namespace
        """
        derived_install_mode = self._derive_install_mode_from_target_namespaces(
            target_namespaces)
        if not self.ohp_obj.is_install_mode_supported_by_channel(
                operator_name, channel_name, derived_install_mode):
            err_msg = f"The specified channel doesn't support {channel_name} installs"
            logger.exception(err_msg)
            raise UnsupportedInstallMode(err_msg)
        else:
            operator_namespace = self._create_operator_namespace(
                operator_name, operator_namespace, channel_name)
            og_name = f"{operator_name}-og"
            assert self.og_obj.create_operator_group(og_name,
                                                     operator_namespace,
                                                     target_namespaces)
        return og_name, operator_namespace

    def add_operator_to_cluster(
            self,
            operator_name: str,
            channel_name: str = '',
            operator_namespace: str = '',
            target_namespaces: Union[List[str], str] = []) -> bool:
        """
        Install an operator in a list of target namespaces
        :param operator_name: (required | str) The name of the operator to be installed
        :param channel_name: (Optional | str) The name of the channel we want to subscribe to. This is what determines
                             what version of the operator we want to install. If left unspecified, the operator default
                             channel is selected.
        :param operator_namespace: (optional | str) The name of the namespace that will hold the subscription
                                   and operatorgroup objects. If left unspecified, a suggested namespace name
                                   will be searched for in the channel object. If not found, a namespace will be
                                   created with the naming convention 'openshift-<operator-name>'
        :param target_namespaces: (optional | list) A list of namespace/Projects where want the
                                   operator to be enabled in. If left unspecified, the operartor
                                   will be installed/enabled throughout the entire cluster.
        :return: (bool) True if install is successful, False otherwise.
        """
        pkg_obj = self.ohp_obj.get_package_manifest(operator_name)
        if not pkg_obj:
            err_msg = f"A package manifest for {operator_name} could not be found"
            logger.exception(err_msg)
            raise ApiException(err_msg)
        if not channel_name:
            channel_name = self.ohp_obj.get_package_default_channel(
                operator_name)
        _, operator_namespace = self._create_og(operator_name, channel_name,
                                                operator_namespace,
                                                target_namespaces)
        sub_resp = self.sub_obj.create_subscription(operator_name,
                                                    channel_name,
                                                    operator_namespace)
        return sub_resp is not None

    def is_operator_installed(self, operator_name: str,
                              operator_namespace: str) -> bool:
        """
        Check if operator is installed and returned true or false
        :param operator_name: name of the operator.
        :param operator_namespace: namespace of the operator
        return: installed or not
        """
        return self.csv.get_cluster_service_version(
            operator_name, operator_namespace) is not None

    def delete_operator_from_cluster(self, operator_name: str,
                                     namespace: str) -> bool:
        """
        Uninstall an operator from a cluster
        :param operator_name: name of the operator
        :param namespace: name of the namespace the operator is installed
        :return: success or failure
        """
        try:
            subscription = self.sub_obj.get_subscription(
                operator_name, namespace)
            csv_name = subscription.status.currentCSV
        except ApiException:
            logger.error("Failed to retrieve subscription")
            return False

        try:
            self.sub_obj.delete_subscription(operator_name, namespace)
            self.csv.delete_cluster_service_version(csv_name, namespace)
        except ApiException:
            logger.error(f"Failed to uninstall operator {operator_name}")
            return False

        return True
def operator_hub(get_kubeconfig) -> OperatorhubPackages:
    return OperatorhubPackages(kube_config_file=get_kubeconfig)