Skip to content

Commit

Permalink
[JENKINS-59679] Use plugin URL from the update site rather than the m…
Browse files Browse the repository at this point in the history
…anifest (#4273)

* Use plugin URL from the update site rather than the manifest

* Avoid Mockito when not needed

* Resolve plugin URL from multiple update sites
  • Loading branch information
zbynek authored and oleg-nenashev committed Nov 10, 2019
1 parent 3a8c1ef commit 40b68ea
Show file tree
Hide file tree
Showing 6 changed files with 159 additions and 26 deletions.
35 changes: 25 additions & 10 deletions core/src/main/java/hudson/PluginWrapper.java
Original file line number Diff line number Diff line change
Expand Up @@ -521,20 +521,30 @@ public String getShortName() {
*/
@Exported
public String getUrl() {
// first look for the manifest entry. This is new in maven-hpi-plugin 1.30
String url = manifest.getMainAttributes().getValue("Url");
if(url!=null) return url;
// first look in update center metadata
List<UpdateSite.Plugin> siteMetadataList = getInfoFromAllSites();
String firstSiteUrl = null;
if (!siteMetadataList.isEmpty()) {
firstSiteUrl = siteMetadataList.get(0).wiki;
if (allUrlsMatch(firstSiteUrl, siteMetadataList)) {
return firstSiteUrl;
}
}

// fallback to update center metadata
UpdateSite.Plugin ui = getInfo();
if(ui!=null) return ui.wiki;
// if update sites give different / empty results,
// use manifest (since maven-hpi-plugin 1.30)
String url = manifest.getMainAttributes().getValue("Url");
if (url != null) {
return url;
}
return firstSiteUrl;
}

return null;
private boolean allUrlsMatch(String url, List<UpdateSite.Plugin> uiList) {
return uiList.stream().allMatch(k -> k.wiki != null && k.wiki.equals(url));
}



@Override
@Override
public String toString() {
return "Plugin:" + getShortName();
}
Expand Down Expand Up @@ -972,6 +982,11 @@ public UpdateSite.Plugin getInfo() {
return uc.getPlugin(getShortName());
}

private List<UpdateSite.Plugin> getInfoFromAllSites() {
UpdateCenter uc = Jenkins.get().getUpdateCenter();
return uc.getPluginFromAllSites(getShortName(), getVersionNumber());
}

/**
* Returns true if this plugin has update in the update center.
*
Expand Down
25 changes: 23 additions & 2 deletions core/src/main/java/hudson/model/UpdateCenter.java
Original file line number Diff line number Diff line change
Expand Up @@ -640,15 +640,36 @@ public String getDefaultBaseUrl() {
}
for (UpdateSite s : sites) {
Plugin p = s.getPlugin(artifactId);
if (p!=null) {
if (minVersion.isNewerThan(new VersionNumber(p.version))) continue;
if (checkMinVersion(p, minVersion)) {
return p;
}
}
return null;
}

/**
* Gets plugin info from all available sites
* @return list of plugins
*/
@Restricted(NoExternalUse.class)
public @Nonnull List<Plugin> getPluginFromAllSites(String artifactId,
@CheckForNull VersionNumber minVersion) {
ArrayList<Plugin> result = new ArrayList<>();
for (UpdateSite s : sites) {
Plugin p = s.getPlugin(artifactId);
if (checkMinVersion(p, minVersion)) {
result.add(p);
}
}
return result;
}

private boolean checkMinVersion(@CheckForNull Plugin p, @CheckForNull VersionNumber minVersion) {
return p != null
&& (minVersion == null || !minVersion.isNewerThan(new VersionNumber(p.version)));
}

/**
* Schedules a Jenkins upgrade.
*/
@RequirePOST
Expand Down
5 changes: 2 additions & 3 deletions core/src/test/java/hudson/PluginWrapperTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -161,12 +161,11 @@ private PluginWrapper buildFailed() {
}

private PluginWrapper build() {
Manifest manifest = mock(Manifest.class);
Attributes attributes = new Attributes();
Manifest manifest = new Manifest();
Attributes attributes = manifest.getMainAttributes();
attributes.put(new Attributes.Name("Short-Name"), name);
attributes.put(new Attributes.Name("Jenkins-Version"), requiredCoreVersion);
attributes.put(new Attributes.Name("Plugin-Version"), version);
when(manifest.getMainAttributes()).thenReturn(attributes);
return new PluginWrapper(
pm,
new File("/tmp/" + name + ".jpi"),
Expand Down
71 changes: 60 additions & 11 deletions test/src/test/java/hudson/model/UpdateSiteTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@

package hudson.model;

import hudson.PluginWrapper;
import hudson.PluginWrapper.Dependency;
import hudson.model.UpdateSite.Data;
import hudson.util.FormValidation;
import hudson.util.PersistedList;
Expand All @@ -32,16 +34,20 @@
import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.jar.Attributes;
import java.util.jar.Manifest;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import static org.junit.Assert.*;

import jenkins.model.Jenkins;
import jenkins.security.UpdateSiteWarningsConfiguration;
import jenkins.security.UpdateSiteWarningsMonitor;
import org.apache.commons.io.FileUtils;
Expand Down Expand Up @@ -107,11 +113,9 @@ public void shutdownWebserver() throws Exception {
}

@Test public void relativeURLs() throws Exception {
PersistedList<UpdateSite> sites = j.jenkins.getUpdateCenter().getSites();
sites.clear();
URL url = new URL(baseUrl, "/plugins/tasks-update-center.json");
UpdateSite site = new UpdateSite(UpdateCenter.ID_DEFAULT, url.toString());
sites.add(site);
overrideUpdateSite(site);
assertEquals(FormValidation.ok(), site.updateDirectly(false).get());
Data data = site.getData();
assertNotNull(data);
Expand All @@ -124,6 +128,28 @@ public void shutdownWebserver() throws Exception {
assertEquals("Wrong name of plugin found", "Task Scanner Plug-in", tasksPlugin.getDisplayName());
}

@Test public void wikiUrlFromSingleSite() throws Exception {
UpdateSite site = getUpdateSite("/plugins/tasks-update-center.json");
overrideUpdateSite(site);
PluginWrapper wrapper = buildPluginWrapper("dummy", "https://wiki.jenkins.io/display/JENKINS/dummy");
assertEquals("https://plugins.jenkins.io/dummy", wrapper.getUrl());
}

@Test public void wikiUrlFromMoreSites() throws Exception {
UpdateSite site = getUpdateSite("/plugins/tasks-update-center.json");
UpdateSite alternativeSite = getUpdateSite("/plugins/alternative-update-center.json", "alternative");
overrideUpdateSite(site, alternativeSite);
// sites use different Wiki URL for dummy -> use URL from manifest
PluginWrapper wrapper = buildPluginWrapper("dummy", "https://wiki.jenkins.io/display/JENKINS/dummy");
assertEquals("https://wiki.jenkins.io/display/JENKINS/dummy", wrapper.getUrl());
// sites use the same Wiki URL for tasks -> use it
wrapper = buildPluginWrapper("tasks", "https://wiki.jenkins.io/display/JENKINS/tasks");
assertEquals("https://plugins.jenkins.io/tasks", wrapper.getUrl());
// only one site has it
wrapper = buildPluginWrapper("foo", "https://wiki.jenkins.io/display/JENKINS/foo");
assertEquals("https://plugins.jenkins.io/foo", wrapper.getUrl());
}

@Test public void updateDirectlyWithJson() throws Exception {
UpdateSite us = new UpdateSite("default", new URL(baseUrl, "update-center.json").toExternalForm());
assertNull(us.getPlugin("AdaptivePlugin"));
Expand All @@ -141,12 +167,8 @@ public void shutdownWebserver() throws Exception {
}

@Test public void incompleteWarningsJson() throws Exception {
PersistedList<UpdateSite> sites = j.jenkins.getUpdateCenter().getSites();
sites.clear();
URL url = new URL(baseUrl, "/plugins/warnings-update-center-malformed.json");
UpdateSite site = new UpdateSite(UpdateCenter.ID_DEFAULT, url.toString());
sites.add(site);
assertEquals(FormValidation.ok(), site.updateDirectly(false).get());
UpdateSite site = getUpdateSite("/plugins/warnings-update-center-malformed.json");
overrideUpdateSite(site);
assertEquals("number of warnings", 7, site.getData().getWarnings().size());
assertNotEquals("plugin data is present", Collections.emptyMap(), site.getData().plugins);
}
Expand Down Expand Up @@ -190,11 +212,38 @@ public void isPluginUpdateCompatible() throws Exception {
assertFalse("isLegacyDefault should be false with null url",new UpdateSite(null,null).isLegacyDefault());
}


private UpdateSite getUpdateSite(String path) throws Exception {
return getUpdateSite(path, UpdateCenter.ID_DEFAULT);
}

private UpdateSite getUpdateSite(String path, String id) throws Exception {
URL url = new URL(baseUrl, path);
UpdateSite site = new UpdateSite(UpdateCenter.ID_DEFAULT, url.toString());
UpdateSite site = new UpdateSite(id, url.toString());
assertEquals(FormValidation.ok(), site.updateDirectly(false).get());
return site;
}

private void overrideUpdateSite(UpdateSite... overrideSites) {
PersistedList<UpdateSite> sites = j.jenkins.getUpdateCenter().getSites();
sites.clear();
sites.addAll(Arrays.asList(overrideSites));
}

private PluginWrapper buildPluginWrapper(String name, String wikiUrl) {
Manifest manifest = new Manifest();
Attributes attributes = manifest.getMainAttributes();
attributes.put(new Attributes.Name("Short-Name"), name);
attributes.put(new Attributes.Name("Plugin-Version"), "1.0.0");
attributes.put(new Attributes.Name("Url"), wikiUrl);
return new PluginWrapper(
Jenkins.get().getPluginManager(),
new File("/tmp/" + name + ".jpi"),
manifest,
null,
null,
new File("/tmp/" + name + ".jpi.disabled"),
null,
new ArrayList<Dependency>()
);
}
}
47 changes: 47 additions & 0 deletions test/src/test/resources/plugins/alternative-update-center.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
updateCenter.post(
{"connectionCheckUrl":"http://www.google.com/", // has to be here because updateDirectly sniffs only very limited formats
"core": {
"buildDate": "Dec 31, 1969",
"name": "core",
"url": "jenkins.war",
"version": "23"
},
"id": "alternative",
"plugins": {
"tasks": {
"buildDate": "Dec 17, 2008",
"dependencies": [],
"developers": [{"name": "lokadm"}],
"excerpt": " This plug-in scans for open tasks in a specified set of files in the project modules and visualizes the results. ",
"name": "tasks",
"wiki": "https://plugins.jenkins.io/tasks",
"requiredCore": "1.264",
"sha1": "wtzlciUKiMcg90H5CTYkGX6+r8Y=",
"title": "Jenkins Task Scanner Plug-in",
"url": "tasks.jpi",
"version": "2.23"
},
"dummy": {
"buildDate": "Dec 17, 2008",
"dependencies": [],
"developers": [],
"excerpt": "",
"name": "dummy",
"wiki": "https://plugins.example.com/dummy",
"requiredCore": "1.100",
"sha1": "wtzlcjUKiMcg90H5CTYkGX6+r8Y=",
"title": "Dummy",
"url": "http://nowhere.net/dummy.hpi",
"version": "1.0"
},
"foo": {
"dependencies": [],
"name": "foo",
"wiki": "https://plugins.jenkins.io/foo",
"version": "1.0",
"url": "http://nowhere.net/dummy.hpi",
}
},
"updateCenterVersion": 1
}
);
2 changes: 2 additions & 0 deletions test/src/test/resources/plugins/tasks-update-center.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ updateCenter.post(
"developers": [{"name": "lokadm"}],
"excerpt": " This plug-in scans for open tasks in a specified set of files in the project modules and visualizes the results. ",
"name": "tasks",
"wiki": "https://plugins.jenkins.io/tasks",
"requiredCore": "1.264",
"sha1": "wtzlciUKiMcg90H5CTYkGX6+r8Y=",
"title": "Jenkins Task Scanner Plug-in",
Expand All @@ -26,6 +27,7 @@ updateCenter.post(
"developers": [],
"excerpt": "",
"name": "dummy",
"wiki": "https://plugins.jenkins.io/dummy",
"requiredCore": "1.100",
"sha1": "wtzlcjUKiMcg90H5CTYkGX6+r8Y=",
"title": "Dummy",
Expand Down

0 comments on commit 40b68ea

Please sign in to comment.