diff --git a/jenkinsapi/jenkins.py b/jenkinsapi/jenkins.py index a004cb26..b37f726e 100644 --- a/jenkinsapi/jenkins.py +++ b/jenkinsapi/jenkins.py @@ -393,9 +393,12 @@ def get_plugins_url(self, depth): # This only ever needs to work on the base object return f"{self.baseurl}/pluginManager/api/python?depth={depth}" + def get_update_center_url(self, depth=1): + return f"{self.baseurl}/manage/updateCenter/api/json?depth={depth}" + def install_plugin( self, - plugin: str | Plugin, + plugin: str | dict | Plugin, restart: bool = True, force_restart: bool = False, wait_for_reboot: bool = True, @@ -403,7 +406,7 @@ def install_plugin( ): """ Install a plugin and optionally restart jenkins. - @param plugin: Plugin (string or Plugin object) to be installed + @param plugin: Plugin (string or dict or Plugin object) to be installed @param restart: Boolean, restart Jenkins when required by plugin @param force_restart: Boolean, force Jenkins to restart, ignoring plugin preferences @@ -646,7 +649,14 @@ def plugins(self): def get_plugins(self, depth: int = 1) -> Plugins: url = self.get_plugins_url(depth=depth) - return Plugins(url, self) + # If the plugins object is not already created or the baseurl has changed + # the we recreate a new one + if ( + not hasattr(self, "_get_plugins") + or self._get_plugins.baseurl != url + ): + self._get_plugins = Plugins(url, self) + return self._get_plugins def has_plugin(self, plugin_name: str) -> bool: return plugin_name in self.plugins diff --git a/jenkinsapi/node.py b/jenkinsapi/node.py index 2c64cac0..0603456f 100644 --- a/jenkinsapi/node.py +++ b/jenkinsapi/node.py @@ -358,7 +358,7 @@ def upload_config(self, config_xml: str) -> None: if self.name == "Built-In Node": raise JenkinsAPIException("Built-In node does not have config.xml") - self.jenkins.requester.post_and_confirm_status( + self.jenkins.requester.post_xml_and_confirm_status( "%(baseurl)s/config.xml" % self.__dict__, data=config_xml ) diff --git a/jenkinsapi/plugin.py b/jenkinsapi/plugin.py index 0ca8b219..937f33e6 100644 --- a/jenkinsapi/plugin.py +++ b/jenkinsapi/plugin.py @@ -19,6 +19,7 @@ def __init__(self, plugin_dict: Union[dict, str]) -> None: self.__dict__ = self.to_plugin(plugin_dict) self.shortName: str = self.__dict__["shortName"] self.version: str = self.__dict__.get("version", "Unknown") + self.url: str = self.__dict__.get("url", None) def to_plugin(self, plugin_string: str) -> dict: plugin_string = str(plugin_string) @@ -37,7 +38,7 @@ def __eq__(self, other) -> bool: return self.__dict__ == other.__dict__ def __str__(self) -> str: - return self.shortName + return f"{self.shortName}@{self.version}" def __repr__(self) -> str: return "<%s.%s %s>" % ( @@ -68,6 +69,9 @@ def is_latest(self, update_center_dict: dict) -> bool: return center_plugin["version"] == self.version def get_download_link(self, update_center_dict) -> str: + if self.url: + return self.url + latest_version = update_center_dict["plugins"][self.shortName][ "version" ] diff --git a/jenkinsapi/plugins.py b/jenkinsapi/plugins.py index 1247ad1b..1af5aab9 100644 --- a/jenkinsapi/plugins.py +++ b/jenkinsapi/plugins.py @@ -46,9 +46,22 @@ def check_updates_server(self) -> None: @property def update_center_dict(self): - update_center = "https://updates.jenkins.io/update-center.json" - jsonp = requests.get(update_center).content.decode("utf-8") - return json.loads(jsonp_to_json(jsonp)) + if not hasattr(self, "_update_center_dict"): + update_center = ( + "https://updates.jenkins.io/update-center.actual.json" + ) + jsonp = requests.get(update_center).content.decode("utf-8") + self._update_center_dict = json.loads(jsonp) + return self._update_center_dict + + @property + def update_center_dict_server(self): + if not hasattr(self, "_update_center_dict_server"): + jsonp = self.jenkins_obj.requester.get_url( + self.jenkins_obj.get_update_center_url(2) + ).content.decode("utf-8") + self._update_center_dict_server = json.loads(jsonp) + return self._update_center_dict_server def _poll(self, tree=None): return self.get_data(self.baseurl, tree=tree) @@ -145,16 +158,11 @@ def _install_specific_version(self, plugin: "Plugin") -> None: download_link: str = plugin.get_download_link( update_center_dict=self.update_center_dict ) - downloaded_plugin: BytesIO = self._download_plugin(download_link) - plugin_dependencies = self._get_plugin_dependencies(downloaded_plugin) - log.debug("Installing dependencies for plugin '%s'", plugin.shortName) - self.jenkins_obj.install_plugins(plugin_dependencies) url = "%s/pluginManager/uploadPlugin" % self.jenkins_obj.baseurl requester = self.jenkins_obj.requester - downloaded_plugin.seek(0) requester.post_and_confirm_status( url, - files={"file": ("plugin.hpi", downloaded_plugin)}, + files={"filename": plugin.shortName, "pluginUrl": download_link}, data={}, params={}, ) @@ -209,6 +217,24 @@ def _plugin_has_finished_installation(self, plugin) -> bool: except JenkinsAPIException: return False # lack of update_center in Jenkins 1.X + def _plugins_has_finished_installation(self) -> bool: + """ + Return True if installation is marked as 'Success' or + 'SuccessButRequiresRestart' in Jenkins' update_center, + else return False. + """ + try: + jobs = self.update_center_install_status["data"]["jobs"] + for job in jobs: + if job["installStatus"] not in [ + "Success", + "SuccessButRequiresRestart", + ]: + return False + return True + except JenkinsAPIException: + return False # lack of update_center in Jenkins 1.X + def plugin_version_is_being_installed(self, plugin) -> bool: """ Return true if plugin is currently being installed.