mirror of https://github.com/apache/lucene.git
SOLR-14604: Add the ability to uninstall a package from with the Package CLI (#1710)
Co-authored-by: Marcus <marcuseagan@gmail.com>
This commit is contained in:
parent
a6c058a260
commit
bd21da6eca
|
@ -116,6 +116,8 @@ New Features
|
||||||
|
|
||||||
* SOLR-14681: Introduce ability to delete .jar stored in the Package Store. (MarcusSorealheis , Mike Drob)
|
* SOLR-14681: Introduce ability to delete .jar stored in the Package Store. (MarcusSorealheis , Mike Drob)
|
||||||
|
|
||||||
|
* SOLR-14604: Add the ability to uninstall a package from with the Package CLI. (MarcusSorealheis)
|
||||||
|
|
||||||
Improvements
|
Improvements
|
||||||
---------------------
|
---------------------
|
||||||
|
|
||||||
|
|
|
@ -28,6 +28,7 @@ import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Scanner;
|
import java.util.Scanner;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
@ -35,8 +36,14 @@ import java.util.stream.Collectors;
|
||||||
|
|
||||||
import org.apache.commons.collections4.MultiValuedMap;
|
import org.apache.commons.collections4.MultiValuedMap;
|
||||||
import org.apache.commons.collections4.multimap.HashSetValuedHashMap;
|
import org.apache.commons.collections4.multimap.HashSetValuedHashMap;
|
||||||
|
import org.apache.http.client.methods.HttpDelete;
|
||||||
|
import org.apache.solr.client.solrj.SolrRequest;
|
||||||
|
import org.apache.solr.client.solrj.SolrServerException;
|
||||||
import org.apache.solr.client.solrj.impl.HttpSolrClient;
|
import org.apache.solr.client.solrj.impl.HttpSolrClient;
|
||||||
|
import org.apache.solr.client.solrj.request.V2Request;
|
||||||
|
import org.apache.solr.client.solrj.request.beans.Package;
|
||||||
import org.apache.solr.client.solrj.request.beans.PluginMeta;
|
import org.apache.solr.client.solrj.request.beans.PluginMeta;
|
||||||
|
import org.apache.solr.client.solrj.response.V2Response;
|
||||||
import org.apache.solr.common.NavigableObject;
|
import org.apache.solr.common.NavigableObject;
|
||||||
import org.apache.solr.common.SolrException;
|
import org.apache.solr.common.SolrException;
|
||||||
import org.apache.solr.common.SolrException.ErrorCode;
|
import org.apache.solr.common.SolrException.ErrorCode;
|
||||||
|
@ -44,6 +51,7 @@ import org.apache.solr.common.cloud.SolrZkClient;
|
||||||
import org.apache.solr.common.cloud.ZkStateReader;
|
import org.apache.solr.common.cloud.ZkStateReader;
|
||||||
import org.apache.solr.common.util.Pair;
|
import org.apache.solr.common.util.Pair;
|
||||||
import org.apache.solr.common.util.Utils;
|
import org.apache.solr.common.util.Utils;
|
||||||
|
import org.apache.solr.filestore.DistribPackageStore;
|
||||||
import org.apache.solr.packagemanager.SolrPackage.Command;
|
import org.apache.solr.packagemanager.SolrPackage.Command;
|
||||||
import org.apache.solr.packagemanager.SolrPackage.Manifest;
|
import org.apache.solr.packagemanager.SolrPackage.Manifest;
|
||||||
import org.apache.solr.packagemanager.SolrPackage.Plugin;
|
import org.apache.solr.packagemanager.SolrPackage.Plugin;
|
||||||
|
@ -85,6 +93,67 @@ public class PackageManager implements Closeable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void uninstall(String packageName, String version) {
|
||||||
|
SolrPackageInstance packageInstance = getPackageInstance(packageName, version);
|
||||||
|
if (packageInstance == null) {
|
||||||
|
PackageUtils.printRed("Package " + packageName + ":" + version + " doesn't exist. Use the install command to install this package version first.");
|
||||||
|
System.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure that this package instance is not deployed on any collection
|
||||||
|
Map<String, String> collectionsDeployedOn = getDeployedCollections(packageName);
|
||||||
|
for (String collection: collectionsDeployedOn.keySet()) {
|
||||||
|
if (version.equals(collectionsDeployedOn.get(collection))) {
|
||||||
|
PackageUtils.printRed("Package " + packageName + " is currently deployed on collection: " + collection + ". Undeploy the package with undeploy <package-name> -collections <collection1>[,<collection2>,...] before attempting to uninstall the package.");
|
||||||
|
System.exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure that no plugin from this package instance has been deployed as cluster level plugins
|
||||||
|
Map<String, SolrPackageInstance> clusterPackages = getPackagesDeployedAsClusterLevelPlugins();
|
||||||
|
for (String clusterPackageName: clusterPackages.keySet()) {
|
||||||
|
SolrPackageInstance clusterPackageInstance = clusterPackages.get(clusterPackageName);
|
||||||
|
if (packageName.equals(clusterPackageName) && version.equals(clusterPackageInstance.version)) {
|
||||||
|
PackageUtils.printRed("Package " + packageName + "is currently deployed as a cluster-level plugin (" + clusterPackageInstance.getCustomData() + "). Undeploy the package with undeploy <package-name> -collections <collection1>[,<collection2>,...] before uninstalling the package.");
|
||||||
|
System.exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete the package by calling the Package API and remove the Jar
|
||||||
|
|
||||||
|
PackageUtils.printGreen("Executing Package API to remove this package...");
|
||||||
|
Package.DelVersion del = new Package.DelVersion();
|
||||||
|
del.version = version;
|
||||||
|
del.pkg = packageName;
|
||||||
|
|
||||||
|
V2Request req = new V2Request.Builder(PackageUtils.PACKAGE_PATH)
|
||||||
|
.forceV2(true)
|
||||||
|
.withMethod(SolrRequest.METHOD.POST)
|
||||||
|
.withPayload(Collections.singletonMap("delete", del))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
try {
|
||||||
|
V2Response resp = req.process(solrClient);
|
||||||
|
PackageUtils.printGreen("Response: " + resp.jsonStr());
|
||||||
|
} catch (SolrServerException | IOException e) {
|
||||||
|
throw new SolrException(ErrorCode.BAD_REQUEST, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
PackageUtils.printGreen("Executing Package Store API to remove the " + packageName + " package...");
|
||||||
|
|
||||||
|
List<String> filesToDelete = new ArrayList<>(packageInstance.files);
|
||||||
|
filesToDelete.add(String.format(Locale.ROOT, "/package/%s/%s/%s", packageName, version, "manifest.json"));
|
||||||
|
for (String filePath: filesToDelete) {
|
||||||
|
DistribPackageStore.deleteZKFileEntry(zkClient, filePath);
|
||||||
|
String path = solrClient.getBaseURL() + "/api/cluster/files" + filePath;
|
||||||
|
PackageUtils.printGreen("Deleting " + path);
|
||||||
|
HttpDelete httpDel = new HttpDelete(path);
|
||||||
|
Utils.executeHttpMethod(solrClient.getHttpClient(), path, Utils.JSONCONSUMER, httpDel);
|
||||||
|
}
|
||||||
|
|
||||||
|
PackageUtils.printGreen("Package uninstalled: " + packageName + ":" + version + ":-)");
|
||||||
|
}
|
||||||
|
|
||||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||||
public List<SolrPackageInstance> fetchInstalledPackageInstances() throws SolrException {
|
public List<SolrPackageInstance> fetchInstalledPackageInstances() throws SolrException {
|
||||||
log.info("Getting packages from packages.json...");
|
log.info("Getting packages from packages.json...");
|
||||||
|
@ -100,10 +169,13 @@ public class PackageManager implements Closeable {
|
||||||
List pkg = (List)packagesZnodeMap.get(packageName);
|
List pkg = (List)packagesZnodeMap.get(packageName);
|
||||||
for (Map pkgVersion: (List<Map>)pkg) {
|
for (Map pkgVersion: (List<Map>)pkg) {
|
||||||
Manifest manifest = PackageUtils.fetchManifest(solrClient, solrBaseUrl, pkgVersion.get("manifest").toString(), pkgVersion.get("manifestSHA512").toString());
|
Manifest manifest = PackageUtils.fetchManifest(solrClient, solrBaseUrl, pkgVersion.get("manifest").toString(), pkgVersion.get("manifestSHA512").toString());
|
||||||
List<Plugin> solrplugins = manifest.plugins;
|
List<Plugin> solrPlugins = manifest.plugins;
|
||||||
SolrPackageInstance pkgInstance = new SolrPackageInstance(packageName.toString(), null,
|
SolrPackageInstance pkgInstance = new SolrPackageInstance(packageName.toString(), null,
|
||||||
pkgVersion.get("version").toString(), manifest, solrplugins, manifest.parameterDefaults);
|
pkgVersion.get("version").toString(), manifest, solrPlugins, manifest.parameterDefaults);
|
||||||
List<SolrPackageInstance> list = packages.containsKey(packageName)? packages.get(packageName): new ArrayList<SolrPackageInstance>();
|
if (pkgVersion.containsKey("files")) {
|
||||||
|
pkgInstance.files = (List) pkgVersion.get("files");
|
||||||
|
}
|
||||||
|
List<SolrPackageInstance> list = packages.containsKey(packageName) ? packages.get(packageName) : new ArrayList<SolrPackageInstance>();
|
||||||
list.add(pkgInstance);
|
list.add(pkgInstance);
|
||||||
packages.put(packageName.toString(), list);
|
packages.put(packageName.toString(), list);
|
||||||
ret.add(pkgInstance);
|
ret.add(pkgInstance);
|
||||||
|
@ -139,18 +211,28 @@ public class PackageManager implements Closeable {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a list of packages that have their plugins deployed as cluster level plugins.
|
* Get a map of packages (key: package name, value: package instance) that have their plugins deployed as cluster level plugins.
|
||||||
* The returned packages also contain the "pluginMeta" from "clusterprops.json" as custom data.
|
* The returned packages also contain the "pluginMeta" from "clusterprops.json" as custom data.
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings({"unchecked"})
|
||||||
public Map<String, SolrPackageInstance> getPackagesDeployedAsClusterLevelPlugins() {
|
public Map<String, SolrPackageInstance> getPackagesDeployedAsClusterLevelPlugins() {
|
||||||
Map<String, String> packageVersions = new HashMap<>();
|
Map<String, String> packageVersions = new HashMap<>();
|
||||||
MultiValuedMap<String, PluginMeta> packagePlugins = new HashSetValuedHashMap<>(); // map of package name to multiple values of pluginMeta (Map<String, String>)
|
MultiValuedMap<String, PluginMeta> packagePlugins = new HashSetValuedHashMap<>(); // map of package name to multiple values of pluginMeta (Map<String, String>)
|
||||||
@SuppressWarnings({"unchecked"})
|
@SuppressWarnings({"unchecked"})
|
||||||
Map<String, Object> result = (Map<String, Object>)Utils.executeGET(solrClient.getHttpClient(),
|
Map<String, Object> result;
|
||||||
solrBaseUrl + PackageUtils.CLUSTERPROPS_PATH, Utils.JSONCONSUMER);
|
try {
|
||||||
|
result = (Map<String, Object>) Utils.executeGET(solrClient.getHttpClient(),
|
||||||
|
solrBaseUrl + PackageUtils.CLUSTERPROPS_PATH, Utils.JSONCONSUMER);
|
||||||
|
} catch (SolrException ex) {
|
||||||
|
if (ex.code() == ErrorCode.NOT_FOUND.code) {
|
||||||
|
result = Collections.emptyMap(); // Cluster props doesn't exist, that means there are no cluster level plugins installed.
|
||||||
|
} else {
|
||||||
|
throw ex;
|
||||||
|
}
|
||||||
|
}
|
||||||
@SuppressWarnings({"unchecked"})
|
@SuppressWarnings({"unchecked"})
|
||||||
Map<String, Object> clusterPlugins = (Map<String, Object>) result.getOrDefault("plugin", Collections.emptyMap());
|
Map<String, Object> clusterPlugins = (Map<String, Object>) result.getOrDefault("plugin", Collections.emptyMap());
|
||||||
for (String key: clusterPlugins.keySet()) {
|
for (String key : clusterPlugins.keySet()) {
|
||||||
// Map<String, String> pluginMeta = (Map<String, String>) clusterPlugins.get(key);
|
// Map<String, String> pluginMeta = (Map<String, String>) clusterPlugins.get(key);
|
||||||
PluginMeta pluginMeta;
|
PluginMeta pluginMeta;
|
||||||
try {
|
try {
|
||||||
|
@ -485,7 +567,7 @@ public class PackageManager implements Closeable {
|
||||||
}
|
}
|
||||||
if (actualValue != null) {
|
if (actualValue != null) {
|
||||||
String expectedValue = PackageUtils.resolve(cmd.expected, pkg.parameterDefaults, overridesMap, systemParams);
|
String expectedValue = PackageUtils.resolve(cmd.expected, pkg.parameterDefaults, overridesMap, systemParams);
|
||||||
PackageUtils.printGreen("Actual: " + actualValue+", expected: " + expectedValue);
|
PackageUtils.printGreen("Actual: " + actualValue + ", expected: " + expectedValue);
|
||||||
if (!expectedValue.equals(actualValue)) {
|
if (!expectedValue.equals(actualValue)) {
|
||||||
PackageUtils.printRed("Failed to deploy plugin: " + plugin.name);
|
PackageUtils.printRed("Failed to deploy plugin: " + plugin.name);
|
||||||
success = false;
|
success = false;
|
||||||
|
@ -516,7 +598,7 @@ public class PackageManager implements Closeable {
|
||||||
}
|
}
|
||||||
if (actualValue != null) {
|
if (actualValue != null) {
|
||||||
String expectedValue = PackageUtils.resolve(cmd.expected, pkg.parameterDefaults, collectionParameterOverrides, systemParams);
|
String expectedValue = PackageUtils.resolve(cmd.expected, pkg.parameterDefaults, collectionParameterOverrides, systemParams);
|
||||||
PackageUtils.printGreen("Actual: "+actualValue+", expected: "+expectedValue);
|
PackageUtils.printGreen("Actual: " + actualValue + ", expected: "+expectedValue);
|
||||||
if (!expectedValue.equals(actualValue)) {
|
if (!expectedValue.equals(actualValue)) {
|
||||||
PackageUtils.printRed("Failed to deploy plugin: " + plugin.name);
|
PackageUtils.printRed("Failed to deploy plugin: " + plugin.name);
|
||||||
success = false;
|
success = false;
|
||||||
|
@ -542,7 +624,7 @@ public class PackageManager implements Closeable {
|
||||||
SolrPackageInstance latest = null;
|
SolrPackageInstance latest = null;
|
||||||
if (versions != null && !versions.isEmpty()) {
|
if (versions != null && !versions.isEmpty()) {
|
||||||
latest = versions.get(0);
|
latest = versions.get(0);
|
||||||
for (int i=0; i<versions.size(); i++) {
|
for (int i=0; i < versions.size(); i++) {
|
||||||
SolrPackageInstance pkg = versions.get(i);
|
SolrPackageInstance pkg = versions.get(i);
|
||||||
if (pkg.version.equals(version)) {
|
if (pkg.version.equals(version)) {
|
||||||
return pkg;
|
return pkg;
|
||||||
|
|
|
@ -46,7 +46,9 @@ public class SolrPackageInstance implements ReflectMapWriter {
|
||||||
|
|
||||||
final public Map<String, String> parameterDefaults;
|
final public Map<String, String> parameterDefaults;
|
||||||
|
|
||||||
@JsonIgnore
|
public List<String> files;
|
||||||
|
|
||||||
|
@JsonIgnore
|
||||||
private Object customData;
|
private Object customData;
|
||||||
|
|
||||||
@JsonIgnore
|
@JsonIgnore
|
||||||
|
|
|
@ -176,6 +176,16 @@ public class PackageTool extends SolrCLI.ToolBase {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case "uninstall": {
|
||||||
|
Pair<String, String> parsedVersion = parsePackageVersion(cli.getArgList().get(1).toString());
|
||||||
|
if (parsedVersion.second() == null) {
|
||||||
|
throw new SolrException(ErrorCode.BAD_REQUEST, "Package name and version are both required. Actual: " + cli.getArgList().get(1));
|
||||||
|
}
|
||||||
|
String packageName = parsedVersion.first();
|
||||||
|
String version = parsedVersion.second();
|
||||||
|
packageManager.uninstall(packageName, version);
|
||||||
|
break;
|
||||||
|
}
|
||||||
case "help":
|
case "help":
|
||||||
case "usage":
|
case "usage":
|
||||||
print("Package Manager\n---------------");
|
print("Package Manager\n---------------");
|
||||||
|
|
|
@ -176,6 +176,15 @@ If a package supports undeploying the plugins it contains (check package author'
|
||||||
$ bin/solr package undeploy <package-name> -collections <collection1>[,<collection2>,...]
|
$ bin/solr package undeploy <package-name> -collections <collection1>[,<collection2>,...]
|
||||||
----
|
----
|
||||||
|
|
||||||
|
=== Uninstall a Package
|
||||||
|
|
||||||
|
If a package has been undeployed or was never deployed, then it can be uninstalled as follows:
|
||||||
|
|
||||||
|
[source,bash]
|
||||||
|
----
|
||||||
|
$ bin/solr package uninstall <package-name>:<package-version>
|
||||||
|
----
|
||||||
|
|
||||||
or
|
or
|
||||||
|
|
||||||
[source,bash]
|
[source,bash]
|
||||||
|
|
Loading…
Reference in New Issue