def condition(self):
     """Gets the object describing the conditions for this build step"""
     node = self._root.find("condition")
     assert node is not None
     plugin = find_plugin(node.attrib["class"])
     if not plugin:
         msg = "Conditional build step condition {0} not supported by PyJen."
         raise NotImplementedError(msg.format(node.attrib["class"]))
     return plugin(node)
Example #2
0
 def condition(self):
     """Gets the object describing the conditions for this build step"""
     node = self._root.find("condition")
     assert node is not None
     plugin = find_plugin(node.attrib["class"])
     if not plugin:
         msg = "Conditional build step condition {0} not supported by PyJen."
         raise NotImplementedError(msg.format(node.attrib["class"]))
     return plugin(node)
def test_unsupported_plugin(caplog):
    with patch("pyjen.utils.plugin_api.iter_entry_points") as entry_points:
        mock_plugin_class = MagicMock(spec=[])
        mock_ep = MagicMock()
        mock_ep.load.return_value = mock_plugin_class
        entry_points.return_value = [mock_ep]

        res = find_plugin("some_plugin")
        assert res is None
        assert "does not expose the required get_jenkins_plugin_name static method" in caplog.text
Example #4
0
def test_unsupported_plugin(caplog):
    with patch("pyjen.utils.plugin_api.iter_entry_points") as entry_points:
        mock_plugin_class = MagicMock(spec=[])
        mock_ep = MagicMock()
        mock_ep.load.return_value = mock_plugin_class
        entry_points.return_value = [mock_ep]

        res = find_plugin("some_plugin")
        assert res is None
        assert "does not expose the required get_jenkins_plugin_name static method" in caplog.text
Example #5
0
 def condition(self):
     """Gets the object describing the conditions for this build step"""
     node = self._root.find("condition")
     assert node is not None
     plugin = find_plugin(node.attrib["class"])
     if not plugin:
         raise PluginNotSupportedError(
             "Conditional build step condition %s not supported by PyJen.",
             node.attrib["class"]
         )
     return plugin(node)
Example #6
0
    def instantiate(json_data, rest_api):
        """Factory method for finding the appropriate PyJen view object based
        on data loaded from the Jenkins REST API

        :param dict json_data:
            data loaded from the Jenkins REST API summarizing the view to be
            instantiated
        :param rest_api:
            PyJen REST API configured for use by the parent container. Will
            be used to instantiate the PyJen view that is returned.
        :returns:
            PyJen view object wrapping the REST API for the given Jenkins view
        :rtype: :class:`~.view.View`
        """
        # TODO: Find some way to cache the json data given inside the view so
        #       we can lazy-load API data. For example, the name of the view
        #       is always included in the json data and therefore queries for
        #       the View name after creation should not require another hit
        #       to the REST API
        log = logging.getLogger(__name__)
        # The default view will not have a valid view URL
        # so we need to look for this and generate a corrected one
        view_url = json_data["url"]
        if '/view/' not in view_url:
            view_url = view_url + "view/" + json_data["name"]

        # Extract the name of the Jenkins plugin associated with this view
        # Sanity Check: make sure the metadata for the view has a "_class"
        #               attribute. I'm pretty sure older version of the Jenkins
        #               core did not expose such an attribute, but all versions
        #               from the past 2+ years or more do appear to include it.
        #               If, for some reason this attribute doesn't exist, we'll
        #               fall back to the default view base class which provides
        #               functionality common to all Jenkins views. For extra
        #               debugging purposes however, we log some debug output
        #               if we ever hit this case so we can investigate the
        #               the details further.
        plugin_class = None
        if "_class" in json_data:
            plugin_class = find_plugin(json_data["_class"])
        else:  # pragma: no cover
            log.debug("Unsupported Jenkins version detected. Views are "
                      "expected to have a _class metadata attribute but this "
                      "one does not: %s", json_data)

        if not plugin_class:
            log.debug("Unable to find plugin for class %s", json_data["_class"])
            plugin_class = View

        return plugin_class(rest_api.clone(view_url))
Example #7
0
    def job(self):
        """Gets the Jenkins job associated with this scheduled build

        May return None if this queue item has been invalidated by Jenkins

        :rtype: :class:`pyjen.job.Job`
        """
        job_data = self._data.get("task")
        if job_data is None:
            return None
        plugin = find_plugin(job_data["_class"])
        if plugin is None:
            raise PluginNotSupportedError(
                "Job plugin not supported.", job_data["_class"])
        return plugin(self._api.clone(job_data["url"]))
Example #8
0
    def triggers(self):
        """list of trigger operations defined for this instance of the plugin

        :rtype: :class:`list` of :class:`BuildTriggerConfig` objects
        """
        retval = list()
        configs_node = self._root.find('configs')
        for config in configs_node:
            plugin = find_plugin(config.tag)
            if plugin is None:
                self._log.warning("Skipping unsupported plugin: %s",
                                  config.tag)
                continue
            retval.append(plugin(config))
        return retval
Example #9
0
def test_one_supported_plugin(caplog):
    with patch("pyjen.utils.plugin_api.iter_entry_points") as entry_points:
        expected_plugin_name = "some_plugin"
        mock_plugin_class = MagicMock()
        mock_plugin_class.get_jenkins_plugin_name.return_value = expected_plugin_name

        mock_ep = MagicMock()
        mock_ep.load.return_value = mock_plugin_class

        entry_points.return_value = [mock_ep]

        res = find_plugin(expected_plugin_name)
        assert res is not None
        assert res == mock_plugin_class
        assert not caplog.text
Example #10
0
def test_one_supported_plugin(caplog):
    with patch("pyjen.utils.plugin_api.iter_entry_points") as entry_points:
        expected_plugin_name = "some_plugin"
        mock_plugin_class = MagicMock()
        mock_plugin_class.get_jenkins_plugin_name.return_value = expected_plugin_name

        mock_ep = MagicMock()
        mock_ep.load.return_value = mock_plugin_class

        entry_points.return_value = [mock_ep]

        res = find_plugin(expected_plugin_name)
        assert res is not None
        assert res == mock_plugin_class
        assert not caplog.text
Example #11
0
 def scm(self):
     """Gets the source code repo where the build script is located"""
     definition_node = self._root.find("definition")
     if "CpsScmFlowDefinition" not in definition_node.attrib["class"]:
         return None
     scm_node = definition_node.find("scm")
     assert scm_node is not None
     assert "class" in scm_node.attrib
     plugin_name = scm_node.attrib["class"]
     plugin = find_plugin(plugin_name)
     if plugin is None:
         raise NotImplementedError(
             "PyJen has no plugin installed for Jenkins plugin: " +
             plugin_name)
     return plugin(scm_node)
Example #12
0
    def job(self):
        """Gets the Jenkins job associated with this scheduled build

        May return None if this queue item has been invalidated by Jenkins

        :rtype: :class:`pyjen.job.Job`
        """
        job_data = self._data.get("task")
        if job_data is None:
            return None
        plugin = find_plugin(job_data["_class"])
        if plugin is None:
            raise NotImplementedError(
                "Job plugin not supported: " + job_data["_class"])
        return plugin(self._api.clone(job_data["url"]))
Example #13
0
    def add_section(self, section_type, name):
        """Adds a new section to the sectioned view

        :param str section_type:
            name of class used to implement the new section to add
        :param str name:
            descriptive text to appear in the title area of the section
        """
        plugin_class = find_plugin(section_type)
        if not plugin_class:
            raise PluginNotSupportedError(
                "Failed loading Sectioned View section", section_type)
        new_section = plugin_class.create(name)
        new_section.parent = self
        sections = self._root.find('sections')
        sections.append(new_section.node)
Example #14
0
    def instantiate(json_data, rest_api):
        """Factory method for finding the appropriate PyJen view object based
        on data loaded from the Jenkins REST API

        :param dict json_data:
            data loaded from the Jenkins REST API summarizing the view to be
            instantiated
        :param rest_api:
            PyJen REST API configured for use by the parent container. Will
            be used to instantiate the PyJen view that is returned.
        :returns:
            PyJen view object wrapping the REST API for the given Jenkins view
        :rtype: :class:`~.view.View`
        """
        # TODO: Find some way to cache the json data given inside the view so
        #       we can lazy-load API data. For example, the name of the view
        #       is always included in the json data and therefore queries for
        #       the View name after creation should not require another hit
        #       to the REST API
        log = logging.getLogger(__name__)

        job_url = json_data["url"]

        # Extract the name of the Jenkins plugin associated with this view
        # Sanity Check: make sure the metadata for the view has a "_class"
        #               attribute. I'm pretty sure older version of the Jenkins
        #               core did not expose such an attribute, but all versions
        #               from the past 2+ years or more do appear to include it.
        #               If, for some reason this attribute doesn't exist, we'll
        #               fall back to the default view base class which provides
        #               functionality common to all Jenkins views. For extra
        #               debugging purposes however, we log some debug output
        #               if we ever hit this case so we can investigate the
        #               the details further.
        plugin_class = None
        if "_class" in json_data:
            plugin_class = find_plugin(json_data["_class"])
        else:  # pragma: no cover
            log.debug("Unsupported Jenkins version detected. Jobs are "
                      "expected to have a _class metadata attribute but this "
                      "one does not: %s", json_data)

        if not plugin_class:
            log.debug("Unable to find plugin for class %s", json_data["_class"])
            plugin_class = Job

        return plugin_class(rest_api.clone(job_url))
Example #15
0
    def add_section(self, section_type, name):
        """Adds a new section to the sectioned view

        :param str section_type:
            name of class used to implement the new section to add
        :param str name:
            descriptive text to appear in the title area of the section
        """
        plugin_class = find_plugin(section_type)
        if not plugin_class:
            raise NotImplementedError(
                "Failed loading Sectioned View section: " +
                section_type)
        new_section = plugin_class.instantiate(name)
        new_section.parent = self
        sections = self._root.find('sections')
        sections.append(new_section.node)
Example #16
0
    def builder(self):
        """Gets the build step managed by this condition
        """
        build_step_node = self._root.find("buildStep")
        plugin = find_plugin(build_step_node.attrib["class"])
        if not plugin:
            raise NotImplementedError(
                "Build step plugin {0} is not supported by PyJen.".format(
                    build_step_node.attrib["class"]))

        # We have to reconstruct the XML for the build step from the
        # encoded version in the buildStep. For further details see
        # the encoding logic found in the create() method of this class.
        root_node = ElementTree.Element(build_step_node.attrib["class"])
        for cur_child in build_step_node:
            root_node.append(cur_child)

        return plugin(root_node)
Example #17
0
    def properties(self):
        """Gets a list of 0 or more Jenkins properties associated with this job

        :returns: a list of customizable properties associated with this job
        :rtype: :class:`list` of property plugins supported by this job
        """
        retval = list()
        nodes = self._root.find('properties')
        for node in nodes:
            plugin = find_plugin(node.tag)
            if plugin is not None:
                temp = plugin(node)
                temp.parent = self
                retval.append(temp)
            else:
                self._log.warning(
                    "Unsupported job 'property' plugin: %s", node.tag)
        return retval
Example #18
0
    def parameters(self):
        """Gets a list of the build parameters associated with this property

        :rtype: :class:`list` of build parameters
        """
        params_node = self._root.find("parameterDefinitions")
        assert params_node is not None
        retval = list()
        for cur_param in params_node:
            plugin = find_plugin(cur_param.tag)

            if plugin is None:
                self._log.warning("Skipping unsupported plugin: %s",
                                  cur_param.tag)
                continue

            retval.append(plugin(cur_param))
        return retval
Example #19
0
    def properties(self):
        """Gets a list of 0 or more Jenkins properties associated with this job

        :returns: a list of customizable properties associated with this job
        :rtype: :class:`list` of property plugins supported by this job
        """
        retval = list()
        nodes = self._root.find('properties')
        for node in nodes:
            plugin = find_plugin(node.tag)
            if plugin is not None:
                temp = plugin(node)
                temp.parent = self
                retval.append(temp)
            else:
                self._log.warning(
                    "Unsupported job 'property' plugin: %s", node.tag)
        return retval
Example #20
0
    def sections(self):
        """
        :returns: a list of all 'section' objects contained in this view
        :rtype: :class:`list` of section plugins associated with this view
        """
        nodes = self._root.find('sections')

        retval = list()
        for node in nodes:
            plugin_class = find_plugin(node.tag)
            if plugin_class is None:
                self._log.warning("Sectioned view plugin not found: %s",
                                  node.tag)
                continue
            temp = plugin_class(node)
            temp.parent = self
            retval.append(temp)

        return retval
Example #21
0
    def builders(self):
        """Gets a list of 0 or more build operations associated with this job

        :returns: a list of build operations associated with this job
        :rtype: :class:`list` of builder plugins used by this job
        """
        retval = list()
        nodes = self._root.find('builders')
        for node in nodes:
            plugin_class = find_plugin(node.tag)
            if plugin_class is None:
                self._log.warning("Unsupported job 'builder' plugin %s",
                                  node.tag)
                continue
            temp = plugin_class(node)
            temp.parent = self
            retval.append(temp)

        return retval
Example #22
0
    def publishers(self):
        """list of 0 or more post-build publishers associated with this job

        :returns: a list of post-build publishers associated with this job
        :rtype: :class:`list` of publisher plugins supported by this job
        """
        retval = list()
        nodes = self._root.find('publishers')
        for node in nodes:
            plugin_class = find_plugin(node.tag)

            if plugin_class is None:
                self._log.warning("Unsupported job 'publisher' plugin: %s",
                                  node.tag)
                continue

            retval.append(plugin_class(node))

        return retval
Example #23
0
    def sections(self):
        """
        :returns: a list of all 'section' objects contained in this view
        :rtype: :class:`list` of section plugins associated with this view
        """
        nodes = self._root.find('sections')

        retval = list()
        for node in nodes:
            plugin_class = find_plugin(node.tag)
            if plugin_class is None:
                self._log.warning("Sectioned view plugin not found: %s",
                                  node.tag)
                continue
            temp = plugin_class(node)
            temp.parent = self
            retval.append(temp)

        return retval
    def builder(self):
        """Gets the build step managed by this condition
        """
        build_step_node = self._root.find("buildStep")
        plugin = find_plugin(build_step_node.attrib["class"])
        if not plugin:
            raise NotImplementedError(
                "Build step plugin {0} is not supported by PyJen.".format(
                    build_step_node.attrib["class"]
                )
            )

        # We have to reconstruct the XML for the build step from the
        # encoded version in the buildStep. For further details see
        # the encoding logic found in the create() method of this class.
        root_node = ElementTree.Element(build_step_node.attrib["class"])
        for cur_child in build_step_node:
            root_node.append(cur_child)

        return plugin(root_node)
Example #25
0
def test_multiple_supported_plugin(caplog):
    with patch("pyjen.utils.plugin_api.iter_entry_points") as entry_points:
        expected_plugin_name = "some_plugin"
        mock_plugin_class1 = MagicMock()
        mock_plugin_class1.get_jenkins_plugin_name.return_value = expected_plugin_name

        mock_plugin_class2 = MagicMock()
        mock_plugin_class2.get_jenkins_plugin_name.return_value = expected_plugin_name

        mock_ep1 = MagicMock()
        mock_ep1.load.return_value = mock_plugin_class1
        mock_ep2 = MagicMock()
        mock_ep2.load.return_value = mock_plugin_class2

        entry_points.return_value = [mock_ep1, mock_ep2]

        res = find_plugin(expected_plugin_name)
        assert res is not None
        assert res == mock_plugin_class1
        assert "multiple plugins detected" in caplog.text
Example #26
0
def test_multiple_supported_plugin(caplog):
    with patch("pyjen.utils.plugin_api.iter_entry_points") as entry_points:
        expected_plugin_name = "some_plugin"
        mock_plugin_class1 = MagicMock()
        mock_plugin_class1.get_jenkins_plugin_name.return_value = expected_plugin_name

        mock_plugin_class2 = MagicMock()
        mock_plugin_class2.get_jenkins_plugin_name.return_value = expected_plugin_name

        mock_ep1 = MagicMock()
        mock_ep1.load.return_value = mock_plugin_class1
        mock_ep2 = MagicMock()
        mock_ep2.load.return_value = mock_plugin_class2

        entry_points.return_value = [mock_ep1, mock_ep2]

        res = find_plugin(expected_plugin_name)
        assert res is not None
        assert res == mock_plugin_class1
        assert "multiple plugins detected" in caplog.text
Example #27
0
    def scm(self):
        """Retrieves the appropriate plugin for the SCM portion of a job

        Detects which source code management tool is being used by this
        job, locates the appropriate plugin for that tool, and returns
        an instance of the wrapper for that plugin pre-configured with
        the settings found in the relevant XML subtree.

        :returns:
            One of any number of plugin objects responsible for providing
            extensions to the source code management portion of a job

            Examples: :class:`~pyjen.plugins.subversion.Subversion`

        :rtype: :class:`~.pluginapi.PluginBase`
        """
        node = self._root.find('scm')
        plugin_class = find_plugin(node.attrib["class"])
        if plugin_class is None:
            raise PluginNotSupportedError("SCM XML plugin not found",
                                          node.attrib["class"])
        return plugin_class(node)